したがって、PEBプロセスからSyshnyランタイムにデータを複製する問題についての考えを表すために、コードを書き留めることにしました。
実際、作成者が抱えている問題を解決するには、いくつかの方法があります-最も簡単な方法は、getenvランタイムのライブラリ関数を放棄し、 kernel32.GetEnvironmentVariableWまたはkernel32.GetEnvironmentStringsWインターフェイスを使用することです。 しかし、トピックをさらに開発して、環境変数を見つけて、特定のプロセスに合わせて環境変数を置き換えることを試みました。
文書化されていないPEB広告を片目で見てみましょう(もちろん、M $はプロパティのかかとを提供しますが、Googleまたはデバッガーを適切に使用すると、すべてが適切に配置されます)。
typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; BOOLEAN Spare; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA LoaderData; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PVOID FastPebLock; PPEBLOCKROUTINE FastPebLockRoutine; PPEBLOCKROUTINE FastPebUnlockRoutine; ULONG EnvironmentUpdateCount; PPVOID KernelCallbackTable; PVOID EventLogSection; PVOID EventLog; PPEB_FREE_BLOCK FreeList; ULONG TlsExpansionCounter; PVOID TlsBitmap; ULONG TlsBitmapBits[0x2]; PVOID ReadOnlySharedMemoryBase; PVOID ReadOnlySharedMemoryHeap; PPVOID ReadOnlyStaticServerData; PVOID AnsiCodePageData; PVOID OemCodePageData; PVOID UnicodeCaseTableData; ULONG NumberOfProcessors; ULONG NtGlobalFlag; BYTE Spare2[0x4]; LARGE_INTEGER CriticalSectionTimeout; ULONG HeapSegmentReserve; ULONG HeapSegmentCommit; ULONG HeapDeCommitTotalFreeThreshold; ULONG HeapDeCommitFreeBlockThreshold; ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; PPVOID *ProcessHeaps; PVOID GdiSharedHandleTable; PVOID ProcessStarterHelper; PVOID GdiDCAttributeList; PVOID LoaderLock; ULONG OSMajorVersion; ULONG OSMinorVersion; ULONG OSBuildNumber; ULONG OSPlatformId; ULONG ImageSubSystem; ULONG ImageSubSystemMajorVersion; ULONG ImageSubSystemMinorVersion; ULONG GdiHandleBuffer[0x22]; ULONG PostProcessInitRoutine; ULONG TlsExpansionBitmap; BYTE TlsExpansionBitmapBits[0x80]; ULONG SessionId; } PEB, *PPEB;
CommandParameterを含むProcessParametersプロパティに興味があります。 環境; また、ring0からring3に複製され、ここからすぐにSyshnomランタイムによってキャッシュされるその他のグッズ。 おそらく、ランタイムは標準のkernel32-> ntdllインターフェイスを使用しており、フックするだけでしたが、セグメントレジスタを介してPEBをプルし、メモリ内の厚かましいものとデータを置き換えることにしました。 置き換えて、Windowsがこれにどのように反応するかを確認してください。 最近、AMD64の下で使用する予定なので、このプラットフォーム専用にコンパイルします。
64ビットプラットフォーム用のインラインasm挿入の可能性を排除したすばらしいM $ソリューションのおかげで、プロジェクト内のアセンブラー関数を
; ; Utils.asm ; INCLUDE Utils.inc .code GetCurrentUserProcessParameters PROC mov rax, gs:[60h] mov rax, [rax + 20h]; ret; GetCurrentUserProcessParameters ENDP END
ちなみに、32ビットバージョンのWindows OSファミリとは異なり、64ビットPEBではgsレジスタを基準としたオフセットによってマッピングされますが、32ビットバージョンでは次のように取得できます。
__declspec(naked) PVOID GetCurrentUserProcessParameters() { __asm { mov eax, fs:[30h]; mov eax, [eax + 10h]; ret; } }
PEBを見ると、32ビットのProcessParametersのオフセットを簡単に計算できます。 4バイトのアライメントでは、16バイトに収まります。 64ビットの場合、8バイトのポインターと4バイトに揃えられた最初のブール値を考慮して、28 + 4バイトが解放されます。 これを確認するには、デバッガを使用してプロセスメモリを調べます。


次に、強度を収集し、プロセスのPEB内のProcessParameters、より具体的には環境変数を置き換えます。 しかし、最初に、Environmentブロックに文字列を保存するためのフォーマットを見てみましょう。 M $は、行がVAR = VALUEの形式であり、ゼロバイトで区切られていることを主張します。ブロックの終わりの符号は、2つのゼロバイトが連続しています。 これを自分の目で確認し、落とし穴を強調します。
- 現在のページのメモリ保護を監視し、なりすましやサービス構造への書き込み後にそれらを復元する必要があります
- イメージの実行可能コード、または挿入されたコードは同じページに配置できるため、実行権を割り当てる必要があることを常に想定する必要があります。 もちろん、これは画像の配置とメモリ割り当ての粒度を考慮して可能性は低いですが、特にマルチスレッド環境で悪い/代替品である場合、安全にプレイすることは不要ではありません
- 作業が終了したら、事前に保存された元のテーブル(テーブルへのポインタ)を復元し、偽のテーブルのメモリを解放することを忘れないでください。
- この例は、変更時にシステムが(別のワークフローから)PEBにアクセスするのを避けるために、エントリポイントで最初の1つでなければならないため、純粋に学術的な例です。 良い場合は、プロセス内のすべてのスレッドを「破棄」し、変更時にそれらを中断する価値があります。
#include <windows.h> #include <stdio.h> #include <conio.h> #include "Utils.h" typedef struct _LSA_UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; typedef struct _RTL_USER_PROCESS_PARAMETERS { ULONG MaximumLength; ULONG Length; ULONG Flags; ULONG DebugFlags; PVOID ConsoleHandle; ULONG ConsoleFlags; HANDLE StdInputHandle; HANDLE StdOutputHandle; HANDLE StdErrorHandle; UNICODE_STRING CurrentDirectoryPath; HANDLE CurrentDirectoryHandle; UNICODE_STRING DllPath; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; PVOID Environment; ULONG StartingPositionLeft; ULONG StartingPositionTop; ULONG Width; ULONG Height; ULONG CharWidth; ULONG CharHeight; ULONG ConsoleTextAttributes; ULONG WindowFlags; ULONG ShowWindowFlags; UNICODE_STRING WindowTitle; UNICODE_STRING DesktopName; UNICODE_STRING ShellInfo; UNICODE_STRING RuntimeData; PVOID DLCurrentDirectory; } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; PVOID ReplacePEBEnvironmentTableAndAddValue(LPCWSTR Variable, LPCWSTR Value) { PRTL_USER_PROCESS_PARAMETERS ProcessParams; MEMORY_BASIC_INFORMATION MemoryInformation; PBYTE NewEnvironment; PWCHAR Token; size_t EnvironmentSize; if (!Variable || !Value || !*Variable || !*Value) return NULL; /* RTL_USER_PROCESS_PARAMETERS PEB */ /* Environment */ /* , , */ /* L'\0' L'\0', MSDN */ /* 4 */ ProcessParams = GetCurrentUserProcessParameters(); Token = (PWCHAR)ProcessParams->Environment; while (!(!*Token && !*(Token + 1))) ++Token; EnvironmentSize = (ULONG_PTR)Token - (ULONG_PTR)ProcessParams->Environment; /* Environment , */ /* , */ MemoryInformation.AllocationProtect = PAGE_EXECUTE_READWRITE; VirtualQuery(ProcessParams->Environment, &MemoryInformation, sizeof(MEMORY_BASIC_INFORMATION)); /* Environment + */ /* */ /* */ /* , Environment, */ /* */ NewEnvironment = (PBYTE)VirtualAlloc(0, EnvironmentSize + 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (NewEnvironment) { /* */ /* Var=Value (+ 2 widechar L'=' ) */ DWORD OldProtect = PAGE_EXECUTE_READWRITE, OldProtect2 = PAGE_EXECUTE_READWRITE; size_t Size = (wcslen(Variable) + wcslen(Value)) * sizeof(WCHAR) + 2 * sizeof(WCHAR); PWCHAR EnvironmentString = malloc(Size); if (EnvironmentString) { /* */ /* */ PVOID OldEnvironment = ProcessParams->Environment; UINT EndOfEnvironment = 0; _snwprintf_s(EnvironmentString, Size / sizeof(WCHAR), _TRUNCATE, L"%ws=%ws", Variable, Value); memcpy(NewEnvironment, ProcessParams->Environment, EnvironmentSize); /* - MSDN */ *((PWCHAR)(NewEnvironment + EnvironmentSize)) = L'\0'; /* */ /* 4 */ memcpy(NewEnvironment + EnvironmentSize + 2, EnvironmentString, Size - sizeof(WCHAR)); memcpy(NewEnvironment + EnvironmentSize + 2 + Size - sizeof(WCHAR), &EndOfEnvironment, 4); /* */ VirtualProtect(NewEnvironment, EnvironmentSize + 0x1000, MemoryInformation.AllocationProtect, &OldProtect); /* RTL_USER_PROCESS_PARAMETERS PEB */ /* */ /* Environment block */ VirtualProtect(ProcessParams, sizeof(RTL_USER_PROCESS_PARAMETERS), PAGE_EXECUTE_READWRITE, &OldProtect); ProcessParams->Environment = NewEnvironment; VirtualProtect(ProcessParams, sizeof(RTL_USER_PROCESS_PARAMETERS), OldProtect, &OldProtect2); /* */ /* Environment block */ free(EnvironmentString); return OldEnvironment; } VirtualFree(NewEnvironment, 0, MEM_RELEASE); } return NULL; } void RestorePEBEnvironmentTable(PVOID OriginalEnvironment) { PRTL_USER_PROCESS_PARAMETERS ProcessParams; DWORD OldProtect = PAGE_EXECUTE_READWRITE, OldProtect2; PVOID OldEnvironment; if (!OriginalEnvironment) return; /* PEB */ ProcessParams = GetCurrentUserProcessParameters(); VirtualProtect(ProcessParams, sizeof(RTL_USER_PROCESS_PARAMETERS), PAGE_EXECUTE_READWRITE, &OldProtect); OldEnvironment = ProcessParams->Environment; ProcessParams->Environment = OriginalEnvironment; VirtualProtect(ProcessParams, sizeof(RTL_USER_PROCESS_PARAMETERS), OldProtect, &OldProtect2); /* , */ VirtualFree(OldEnvironment, 0, MEM_RELEASE); } void main(int argc, char *argv[]) { PVOID OriginalEnvironment; UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); OriginalEnvironment = ReplacePEBEnvironmentTableAndAddValue(L"NewVar", L"NewValue"); if (OriginalEnvironment) { WCHAR Buff[1024] = {0}; if (GetEnvironmentVariableW(L"NewVar", Buff, sizeof(Buff))) wprintf_s(L"GetEnvironmentVariableW(): NewVar == %ws\n", Buff); printf_s("Restoring PEB Environment Table...\n"); RestorePEBEnvironmentTable(OriginalEnvironment); if (!GetEnvironmentVariableW(L"NewVar", Buff, sizeof(Buff))) printf_s("GetEnvironmentVariableW(): NewVar not found\n"); } _getch(); }
その後、結論は次のとおりです。

「ネイティブ」テーブルに戻っても、キャッシュは再構築されません。 したがって、適切なkernel32インターフェースを使用して環境変数のブロックを操作することにより、dllに関する著者の問題は解決されます。
最も興味深いのは、サードパーティのプロセスでデータを変更するときにこのメソッドが機能することです。これは後で表示できます。
更新: CleverMouseが正しく指摘したように、Cライブラリを介した環境変数の操作は、特定の例では意味を持ちません。メイン関数の場合、_wenvironではなく_environキャッシュがいっぱいになるため、これをデバッグ出力として検討することをお勧めします。
永遠にあなたのもの
rwx64