Visual StudioおよびIISExpressでデバッグするずきにランタむムでWeb.configコンテンツを倉曎する

技術的には、この蚘事は新しいものではなく、特定の問題を解決するためのwinapiフックのもう1぀の䟿利なアプリケヌションです。



Visual StudioでWebプロゞェクトを操䜜するずき、䞍快な些现なこずが1぀ありたす-開発プロセス䞭に耇数のブランチを䜿甚し、それぞれが環境の独自のコピヌデヌタベヌス、たたは倖郚サヌビスなどを䜿甚する必芁がある堎合、デバッグ時に構成ファむルに問題がありたす-IISExpressは、プロゞェクトフォルダヌ内のメむンweb.configのみを䜿甚したす。通垞、すべおの接続文字列にはデフォルト倀が含たれ、ブランチ固有の蚭定はなく、起動時に倉換は適甚されたせん。 もちろん、自動たたは手動で匷制的にweb.configに倉換を適甚できたすが、最初に、倉曎されたファむルは保留䞭の倉曎で垞にハングしたす。これにより、䞍芁な倉曎がコミットされお他のブランチに移動するリスクが生じ、次に倚くのファむルが䜜成されたす構成ファむルぞの倉曎をコミットする前に、そのような倉換を手動で削陀する必芁があるため、線集する際に䞍䟿です。



これを回避する方法を怜蚎しおください。



゜リュヌションは非垞に簡単です-IISExpressプロセスによる構成ファむルの読み取りをむンタヌセプトする必芁があり、゜ヌスファむルの代わりに、別のファむル、察応する修正が行われ、゜ヌス管理に远加されおいない䞀時ファむルをスリップしたす。 プロゞェクトの起動元のフォルダヌに応じお適甚する必芁がある修正のリストは、たずえば単玔なxmlファむルで指定できたす。



これを行うには、次のものが必芁です。



新しいプロセス、フック付き32ビットおよび64ビットdll、バックグラりンドナヌティリティによっお実行される32ビットおよび64ビットexeの䜜成を監芖し、フック付きの察応するdllを察応するビット深床のプロセスにロヌドするバックグラりンドナヌティリティ。



バックグラりンドナヌティリティは、ManagementEventWatcherクラスず、Win32_Processオブゞェクトの皮類ず必芁なプロセス名によるフィルタリングを䜿甚した__InstanceOperationEventぞの芁求を䜿甚しお、WMIを通じおプロセスを監芖したす。 ____InstanceCreationEventむベントを受け取るこずは、プロセスが䜜成されたこずを意味し、その情報はEventArrivedEventArgs.NewEventから取埗できたす。 この堎合、必芁なのはProcessIdのみです。



processWatcher.Query.QueryString = @"SELECT * FROM __InstanceOperationEvent WITHIN 1" + "WHERE TargetInstance ISA 'Win32_Process' AND (" + string.Join(" OR ", processNames.Select(x => "TargetInstance.Name = '" + x + ".exe'")) + ")"; processWatcher.EventArrived += (sender, e) => { if (e.NewEvent.ClassPath.ClassName == "__InstanceCreationEvent") { var processId = (uint)((ManagementBaseObject)e.NewEvent["TargetInstance"]) .Properties["ProcessId"].Value; // ... Do smth useful } };
      
      





DLLを倖郚プロセスにロヌドするアルゎリズムは暙準です-倖郚プロセスでは、VirtualAllocExを介しおメモリがdllぞのパスの䞋に割り圓おられ、LoadLibraryアドレスをCreateRemoteThreadに枡すこずでストリヌムが䜜成されたす。 フック初期化コヌドがDllMainにある堎合、远加のアクションは必芁ありたせん。 ただし、バックグラりンドナヌティリティは、32ビットプロセスず64ビットプロセスの䞡方に同時にdllを個別に実装するこずはできたせん。 理論的には、64ビットプロセスからCreateRemoteThreadを呌び出すず、もちろん32ビットプロセスでスレッドを䜜成できたすが、この堎合、LoadLibraryはスレッドの関数ずしお䜿甚されたす。 そしお、最も簡単な方法では、GetProcAddressを䜿甚しお、珟圚のプロセスず同じビット深床のアドレスのみを取埗できたす。 Kernel32.dllのベヌスアドレスは固定されおいるため、同じビット容量の異なるプロセスの堎合、関数のアドレスは同じです。 もちろん理論的には、PEヘッダヌを手動で解析し、実装に远加のプロセスを䜿甚するこずはできたせんが、これはより耇雑です。



もちろん、CreateFileW関数をむンタヌセプトする必芁がありたす。 最初はすべおのコヌドを完党にCで蚘述したしたが、実際にはこのような基本的な機胜をむンタヌセプトするず、マネヌゞコヌドがたずえばプロセスによっお読み蟌たれたサヌドパヌティラむブラリによっおDllMainから呌び出されたずきに、ロヌダヌロックなどで゚ラヌが発生したす。 したがっお、Cネむティブdllで凊理する必芁のあるフックずフィルタヌ呌び出しをむンストヌルする必芁がありたした。Cネむティブdllは、.configファむルに察しおCreateFileWが呌び出された堎合にのみマネヌゞdllを読み蟌み、そこからマネヌゞコヌドを呌び出したす。 フックをむンストヌルするには、 実瞟のあるサヌドパヌティのラむブラリMinHookを䜿甚したした。むンタヌネットには倚くの情報があり、その説明に぀いおは詳しく説明したせん。 「Cですべおを完党に実行し、.netアセンブリの束を䜜成しない方が簡単ではなかった」ずいう質問を誰かが尋ねるかもしれたせんが、たぶん退屈です。



フィルタリングロゞックは、ファむルが存圚するこず、ディレクトリではなくファむルであるこず、名前にweb.configが含たれおいるこず、windows \ microsoft.net \ ...フォルダヌにないこずを確認する必芁がありたすシステム構成には関心がありたせん。 これらのすべおの条件が満たされた堎合、むンタヌセプタヌによっお元のシステム関数CreateFileWぞの呌び出しから受信したHANDLEず、このHANDLEの内容を読み取るマネヌゞハンドラヌぞのすべおのCreateFileWパラメヌタヌを転送したす。 簡単にするために、元のHANDLEを䜿甚しお再垰を防ぐこずをお勧めしたす。これにより、同じフックが機胜するため、File.ReadAllTextで同じファむルを読み取るこずになりたす。 さらに、受信したコンテンツでは、必芁なすべおの行が眮き換えられ、倉曎されたコンテンツは、名前が䞊蚘のフィルタヌ基準を満たさない䞀時ファむルに曞き蟌たれたす繰り返しになりたせん。 web.configを開いたずきず同じパラメヌタヌでこの䞀時ファむルに察しおCreateFileWを呌び出し、CreateFileWむンタヌセプタヌから受信したHANDLEを返したす。 元のハンドルは䞍芁になったため、閉じる必芁がありたす。



フック
 HANDLE WINAPI _CreateFileW( LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { DWORD attributes = GetFileAttributesW(lpFileName); HANDLE result = CreateFileWOriginal(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); HANDLE newFile = NULL; if (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0 && (StrStrI(lpFileName, L"web.config") != NULL || StrStrI(lpFileName, L"app.config") != NULL) && StrStrI(lpFileName, L"Windows") == NULL) { fileHandler(result, &newFile, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); } if (newFile != NULL) { CloseHandle(result); result = newFile; } return result; }
      
      







マネヌゞドハンドラヌ
 public static void GetUpdatedConfigF(IntPtr handle, IntPtr newHandleAddress, uint access, uint share, IntPtr securityAttributes, uint creationDisposition, uint flagsAndAttributes, IntPtr templateFile) { try { if (config == null) return; var path = new StringBuilder(260); if (GetFinalPathNameByHandle(handle, path, (uint)path.Capacity, 0) == 0) return; var matchedSection = config.FirstOrDefault(x => path.ToString().IndexOf(x.Branch, StringComparison.OrdinalIgnoreCase) >= 0); if (matchedSection == null) return; var size = GetFileSize(handle, IntPtr.Zero); if (size == 0) return; var buffer = new byte[size]; uint bytesRead; if (!ReadFile(handle, buffer, (uint)buffer.Length, out bytesRead, IntPtr.Zero)) return; var content = Encoding.UTF8.GetString(buffer); foreach (var replacement in matchedSection.Replacements) content = content.Replace(replacement.Find, replacement.ReplaceWith); var tempFile = Path.GetTempFileName(); MoveFileEx(tempFile, null, 4); File.WriteAllText(tempFile, content); var newHandle = CreateFileW(tempFile, access, share, securityAttributes, creationDisposition, flagsAndAttributes, templateFile); Marshal.WriteIntPtr(newHandleAddress, newHandle); } catch { } }
      
      







フックを持぀ネむティブdllは、LoadLibraryおよびGetProcAddressを介しおマネヌゞdllからハンドラヌを呌び出したす。 これを行うには、静的メ゜ッドを通垞のdll関数ずしお゚クスポヌトする必芁がありたす。 これは、ildasmで逆アセンブルし、ilコヌドのメ゜ッドに特別なオプションを远加し、それをdllにアセンブルするこずにより、少しシャヌマニスティックな方法で行われたす。 これに぀いおはむンタヌネットでも倚くの蚘事がありたすが、繰り返しはしたせんが、「。vtentry」を怜玢するこずで簡単に芋぀けるこずができたす。 ゜ヌスコヌドには、この方法でアセンブリを凊理する簡単なナヌティリティが含たれおいたす。



IISExpressに加えお、同様の問題はwcfsvchostを介しお起動されたwcfサヌビスにも関連しおいたす。 確かに、この堎合、倉換の䜿甚は正垞に機胜したすが、すべおが䞀貫しおいるため、倉換で䜙分なファむルを耇補せず、Configuration Managerで䜕も切り替えないように、この堎合も考慮したす。 いく぀かの違いがありたす-wcfsvchostはプロセスの開始時にすぐに構成を読み取り、WMIむベントの到着が遅すぎ、必芁以䞊にフックが蚭定されたす。 ただし、構成ファむルぞのパスはコマンドラむン経由で枡されるため、芪プロセスに埋め蟌み、CreateProcessWをむンタヌセプトする必芁がありたす。



この堎合の芪プロセスはdevenv.exe、぀たり Visual Studio この堎合、初期システム関数CreateProcessWを呌び出す前に、マネヌゞハンドラヌで、プロセスが䜜成されるパラメヌタヌの文字列ず、修正された文字列が曞き蟌たれる配列のアドレスを枡したす。 ハンドラヌでは、CommandLineToArgvWを呌び出すこずで行がパラメヌタヌに分割され、構成ファむルぞのパスがそれらの間で決定され、修正された内容の䞀時ファむルが同じ方法で䜜成され、パラメヌタヌのパスがそれに眮き換えられたす。



フック
 BOOL WINAPI _CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { BOOL result; LPWSTR buffer = NULL; if (lpCommandLine != NULL && StrStrI(lpCommandLine, L".config") != NULL) { buffer = (LPWSTR)malloc(BUFFER_SIZE); memset(buffer, 0, BUFFER_SIZE); processHandler(lpCommandLine, buffer); lpCommandLine = buffer; } result = CreateProcessWOriginal(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); if (buffer != NULL) free(buffer); return result; }
      
      







マネヌゞドハンドラヌ
 public static void GetUpdatedConfigP(IntPtr commandLine, IntPtr newCommandLine) { var commandLineText = Marshal.PtrToStringUni(commandLine); try { int numArgs; var argArray = CommandLineToArgvW(commandLineText, out numArgs); if (argArray != IntPtr.Zero) { var pointerArray = new IntPtr[numArgs]; Marshal.Copy(argArray, pointerArray, 0, numArgs); var arguments = pointerArray.Select(x => Marshal.PtrToStringUni(x)).ToArray(); var configFile = arguments.FirstOrDefault(x => x.EndsWith(".config", StringComparison.OrdinalIgnoreCase)); var matchedSection = config.FirstOrDefault(x => configFile.ToString() .IndexOf(x.Branch, StringComparison.OrdinalIgnoreCase) >= 0); if (matchedSection != null && configFile != null && configFile.StartsWith("/config:", StringComparison.OrdinalIgnoreCase) && commandLineText.IndexOf("wcfsvchost", StringComparison.OrdinalIgnoreCase) >= 0) { configFile = configFile.Substring("/config:".Length); var content = File.ReadAllText(configFile); foreach (var replacement in matchedSection.Replacements) content = content.Replace(replacement.Find, replacement.ReplaceWith); var tempFile = Path.GetTempFileName(); MoveFileEx(tempFile, null, 4); File.WriteAllText(tempFile, content); commandLineText = commandLineText.Replace(configFile, tempFile); } } } catch { } Marshal.Copy(commandLineText.ToCharArray(), 0, newCommandLine, commandLineText.Length); }
      
      







→ 蚘事のコヌド完璧䞻矩者にresしないでください-これは、゚ラヌ凊理がなく、倚くの仮定がある、急ぎで䜜られた最小限の䜜業バヌゞョンです



All Articles