{"id":224,"date":"2021-07-28T18:54:33","date_gmt":"2021-07-28T16:54:33","guid":{"rendered":"https:\/\/theredwindows.net\/?p=224"},"modified":"2021-07-28T20:13:07","modified_gmt":"2021-07-28T18:13:07","slug":"psreflect-ftw","status":"publish","type":"post","link":"https:\/\/theredwindows.net\/index.php\/2021\/07\/28\/psreflect-ftw\/","title":{"rendered":"PSReflect FTW"},"content":{"rendered":"\n<p>Bien que les possibilit\u00e9s offertes par .NET sont extr\u00eamement puissantes, il est parfois n\u00e9cessaire d\u2019avoir une interaction directe avec la WinAPI. En C#, le Pinvoke y est d\u00e9di\u00e9. Dans cet article, nous verrons comment faire une impl\u00e9mentation discr\u00e8te, stable et viable du Pinvoke. L\u2019exemple principal de ce poste sera celui d\u2019un outil que j\u2019ai \u00e9crit, SweetBackup. Je dois vous avertir cependant qu\u2019il ne s\u2019agit pas d\u2019un article ais\u00e9 de compr\u00e9hension, il vous faudra une bonne connaissance de PowerShell et de la r\u00e9flexion.<\/p>\n\n\n\n<h2>PInvoke<\/h2>\n\n\n\n<p>Pour \u00eatre tout \u00e0 fait rigoureux, je devrais l\u2019appeler P\/Invoke mais je risque d\u2019utiliser PInvoke. Le &#8220;platform Invoke&#8221; est une impl\u00e9mentation dans le framework .NET qui permet l\u2019utilisation de code natif de dlls (aussi appel\u00e9 code non manag\u00e9). Autrement dit, l\u2019acc\u00e8s \u00e0 la WinAPI. En C#, on utilise l&#8217;espace de nom <code>System.Runtime.InteropServices<\/code> qui exporte un attribut <code data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">[DllImport()]<\/code>. Voici un exemple pour la fonction <code>AdjustTokenPrivilege<\/code>, en C#.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">[DllImport(\"advapi32.dll\", SetLastError=true)]\n[return: MarshalAs(UnmanagedType.Bool)]\nstatic extern bool AdjustTokenPrivileges(IntPtr TokenHandle,\n   [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,\n   ref TOKEN_PRIVILEGES NewState,\n   UInt32 BufferLengthInBytes,\n   ref TOKEN_PRIVILEGES PreviousState,\n   out UInt32 ReturnLengthInBytes);<\/pre>\n\n\n\n<p>Ou encore possible en VB:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"visualbasic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;DllImport(\"advapi32.dll\", SetLastError:=True)> _\nPrivate Function AdjustTokenPrivileges( _\n    ByVal TokenHandle As IntPtr, _\n    ByVal DisableAllPrivileges As Boolean, _\n    ByRef NewState As TOKEN_PRIVILEGES, _\n    ByVal Zero As Integer, _\n    ByVal Null1 As IntPtr, _\n    ByVal Null2 As IntPtr _\n  ) As Boolean<\/pre>\n\n\n\n<p>Pinvoke est tr\u00e8s pratique car il permet d\u2019\u00e9tendre les fonctionnalit\u00e9s de .NET, et puisqu\u2019il est possible de l\u2019utiliser en m\u00e9moire (sans toucher le disque) il devient d\u2019autant plus puissant. En effet, tous les programmes qui n\u00e9cessitaient d\u2019\u00eatre \u00e9crit en C++ ou en C peuvent d\u00e9sormais avoir une impl\u00e9mentation en code manag\u00e9. En tant qu\u2019attaquant, il y a donc tout int\u00e9r\u00eat \u00e0 l\u2019utiliser. Mais comment fonctionne ce fameux attribut ? Il va dans un premier temps chercher la dll, puis il va utiliser <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">LoadLibrary()<\/code> pour la charger en m\u00e9moire, et enfin utiliser la fonction <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">GetProcAddress()<\/code> pour charger la fonction. Bien que les derni\u00e8res fonctions cit\u00e9es soient des fonctions natives, elles sont accessible via la dll <code>mscoree.dll<\/code> qui est la librairie o\u00f9 CLR est impl\u00e9ment\u00e9.<\/p>\n\n\n\n<h2>\u00c9quivalence de type<\/h2>\n\n\n\n<p>Avant de rentrer dans le c\u0153ur du sujet, il est n\u00e9cessaire de parler objet. Les types qu\u2019utilisent C# et PowerShell sont des objets, or ce n\u2019est absolument pas le cas du C, o\u00f9 un type ne repr\u00e9sente qu\u2019un espace en m\u00e9moire. Il faut donc \u00e9tablir une \u00e9quivalence entre les types .NET et les types non manag\u00e9s pour pouvoir appeler des fonctions natives. Petit point d\u00e9finition, on appelle &#8220;marshaling&#8221; l\u2019op\u00e9ration de transformation d\u2019un objet en un ensemble de type plus simple (on retrouve ainsi la d\u00e9finition de la s\u00e9rialisation). Voici un tableau assez complet\u00a0:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Nom dans la<br>documentation Microsoft<\/td><td>Nom du type en C<\/td><td>Nom du type en C#<\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td> <code>VOID<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void<\/code><\/td><td><code>System.Void<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>HANDLE<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">void *<\/code><\/td><td><code>System.IntPtr<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>BYTE<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">unsigned char<\/code><\/td><td><code>System.Byte<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>SHORT<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">short<\/code><\/td><td><code>System.Int16<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>WORD<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">unsigned short<\/code><\/td><td><code>System.UInt16<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>INT<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">int<\/code><\/td><td><code>System.Int32<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>UINT<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">unsigned int<\/code><\/td><td><code>System.UInt32<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>LONG<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">long<\/code><\/td><td><code>System.Int32<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>BOOL<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">long<\/code><\/td><td><code>System.Bool<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>DWORD<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">unsigned long<\/code><\/td><td><code>System.UInt32<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>ULONG<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">unsigned long<\/code><\/td><td><code>System.UInt32<\/code> <\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>CHAR<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">char<\/code><\/td><td><code>System.Char<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>WCHAR<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">wchar_t<\/code><\/td><td> <code>System.Char<\/code> <\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>LPSTR<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">char *<\/code><\/td><td><code>System.String<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>LPCSTR<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">const char *<\/code><\/td><td> <code>System.String <\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>LPWSTR<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">wchar_t *<\/code><\/td><td><code>System.String <\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>FLOAT<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">float<\/code><\/td><td><code>System.Single<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>DOUBLE<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">double<\/code><\/td><td><code>System.Double<\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><tr><td><code>LPCWSTR<\/code><\/td><td><code data-enlighter-language=\"c\" class=\"EnlighterJSRAW\">const wchar_t *<\/code><\/td><td><code>System.String <\/code><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><\/tbody><\/table><figcaption><br>Les types System.String peuvent \u00eatre remplac\u00e9 par System.Text.StringBuilder<br>r\u00e9f\u00e9rence: <a href=\"https:\/\/docs.microsoft.com\/fr-fr\/dotnet\/framework\/interop\/marshaling-data-with-platform-invoke\">https:\/\/docs.microsoft.com\/fr-fr\/dotnet\/framework\/interop\/marshaling-data-with-platform-invoke<\/a><\/figcaption><\/figure>\n\n\n\n<p>Si un pointeur est requis, il faudra simplement ajouter <code>.MakeByRefType()<\/code> apr\u00e8s le type, et on utilisera <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[ref]<\/code> devant la variable qui contient le contient. Lorsqu\u2019il s\u2019agira de structures ou d\u2019\u00e9num\u00e9rations, il sera alors n\u00e9cessaire de les d\u00e9finir au pr\u00e9alable.<\/p>\n\n\n\n<h2>La mani\u00e8re la plus simple<\/h2>\n\n\n\n<p>Dans le but de faire du Pinvoke depuis PowerShell, le plus simple est d\u2019ajouter du code .NET \u00e0 la session actuelle. Vous vous rappelez peut-\u00eatre alors que <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Add-Type<\/code>, est une Cmdlet qui permet de r\u00e9pondre \u00e0 ce besoin. L\u2019avantage de cette m\u00e9thode est sa simplicit\u00e9. En effet, les signatures des fonctions \u00e0 importer sont les m\u00eames que celles en C#. En cela, notre travail r\u00e9sidera seulement \u00e0 aller sur <a rel=\"noreferrer noopener\" href=\"https:\/\/pinvoke.net\/\" data-type=\"URL\" data-id=\"https:\/\/pinvoke.net\/\" target=\"_blank\">pinvoke.net<\/a> chercher notre fonction, et l\u2019importer. Voici un exemple avec la <code>MessageBox<\/code> qui permet d\u2019ouvrir une boite de dialogue contenant un message.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$Signature = @'\n    [DllImport(\"user32.dll\", CharSet = CharSet.Unicode, EntryPoint = \"MessageBoxW\", ExactSpelling = true)]\n    public static extern int MessageBox(IntPtr hWnd, string text, string caption, int type);\n'@\n\nAdd-Type -MemberDefinition $Signature -Name User32 -Namespace Win32Functions\n\n[Win32Functions.User32]::MessageBox([IntPtr]::Zero, \"hello\", \"PInvoke\", 0x40)<\/pre>\n\n\n\n<p>Cependant, <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Add-Type<\/code> laisse des fichiers de compilation et appelle un processus non-trivial <code>csc.exe<\/code> qui n\u2019est pas utilis\u00e9 pour autre chose que cela. Pour importer son code, il va d\u2019abord le compiler, et l\u2019ajouter en utilisant <code>System.Reflection<\/code>. Ce qui, dans une perspective de discr\u00e9tion, n\u2019est clairement pas recommandable. Ainsi, on souhaite une meilleure m\u00e9thode pour acc\u00e9der \u00e0 du code non manag\u00e9. <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/mattifestation\" data-type=\"URL\" data-id=\"https:\/\/twitter.com\/mattifestation\" target=\"_blank\">@Mattifestation<\/a> nous a alors offert le fruit de son travail: <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/mattifestation\/PSReflect\" data-type=\"URL\" data-id=\"https:\/\/github.com\/mattifestation\/PSReflect\" target=\"_blank\">PSReflect<\/a>.<\/p>\n\n\n\n<h2>PSReflect<\/h2>\n\n\n\n<p>PSReflect est assur\u00e9ment la mani\u00e8re la plus \u00e9l\u00e9gante de faire du Pinvoke en PowerShell. Il se base principalement sur les pouvoirs de la r\u00e9flexion puisqu\u2019il va utiliser l\u2019attribut <code data-enlighter-language=\"csharp\" class=\"EnlighterJSRAW\">[DllImport()]<\/code>. L\u2019id\u00e9e g\u00e9n\u00e9rale de cette librairie est &#8220;d\u2019\u00e9muler une syntaxe type C&#8221;. Ainsi beaucoup de fonctions ne sont pas des fonctions Cmdlets (aussi appel\u00e9es fonctions avanc\u00e9es). Le module exporte les fonctions suivantes:<\/p>\n\n\n\n<ul><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">New-InMemoryModule<\/code> qui permet de cr\u00e9er un nouvel espace de nom .NET ainsi qu\u2019un module uniquement en m\u00e9moire.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">func<\/code> qui permet de faciliter la cr\u00e9ation d\u2019objets utilis\u00e9s par la fonction suivante.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Add-Win32Type<\/code> qui permet de cr\u00e9er un type .NET repr\u00e9sentant une fonction native. Ce dernier type devra \u00eatre ajout\u00e9 \u00e0 un module pour pouvoir \u00eatre utilis\u00e9.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">psenum<\/code> qui permet de cr\u00e9er une \u00e9num\u00e9ration \u00e0 l\u2019image des \u00e9num\u00e9rations en C.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">struct<\/code> qui permet de cr\u00e9er des structures \u00e0 l\u2019image des structures en C.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">field<\/code> qui permet de simplifier la cr\u00e9ation de champ pour <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">struct<\/code>.<\/li><\/ul>\n\n\n\n<p>Dans la suite on explorera chacune de ces fonctions pour mieux les comprendre, et ainsi avoir une plus vision plus claire lors de leur utilisation. Mais dans un premier temps, essayons d\u2019utiliser la r\u00e9flexion pour manuellement charger une fonction. Nous prendrons par simplicit\u00e9 <code>MessageBox<\/code> qui ne n\u00e9cessite point grand chose. On commence par cr\u00e9er un module en m\u00e9moire dynamique. L\u2019id\u00e9e \u00e9tant de rendre accessible notre fonction gr\u00e2ce \u00e0 ce module. Pour cela on cr\u00e9er un objet de classe <code>System.Reflection.AssemblyName<\/code>, que l\u2019on passe \u00e0 la m\u00e9thode <code>.DefineDynamicAssembly()<\/code>. Puis on cr\u00e9er le module avec <code>.DefineDynamicModule()<\/code> auquel on vient finalement cr\u00e9er un une classe \u00e0 l\u2019aide de <code>.DefineType()<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$DynAssembly = New-Object System.Reflection.AssemblyName('Win32Lib')\n$AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)\n$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Win32Lib', $False)\n$TypeBuilder = $ModuleBuilder.DefineType('User32', 'Public, Class')<\/pre>\n\n\n\n<p>On ajoute alors une m\u00e9thode \u00e0 notre type, qui exhibera notre fonction. Pour cela, on se sert de notre variable <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$TypeBuilder<\/code> et on utilise la m\u00e9thode <code>.DefineMethod()<\/code>. Le premier param\u00e8tre sera le nom de la m\u00e9thode, puis les attributs de cette m\u00e9thode, qui ici seront <code>Public<\/code> et <code>Static<\/code>. On indique apr\u00e8s le type de retour, et enfin un tableau <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[Type[]]<\/code> qui donne les types des param\u00e8tres de notre m\u00e9thode.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$PInvokeMethod = $TypeBuilder.DefineMethod(\n\t'MessageBox',\n\t[Reflection.MethodAttributes] 'Public, Static',\n\t[Int32],\n\t[Type[]] @([IntPtr], [String], [String], [Int32])\n)<\/pre>\n\n\n\n<p>Nous devons ensuite cr\u00e9er un attribut <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[DllImport()]<\/code> avec ses param\u00e8tres, exactement comme en C# (en version r\u00e9flexion). On commence par cr\u00e9er un attribut \u00e0 l&#8217;aide de son constructeur. Puis on charge les param\u00e8tres de cet attribut que l\u2019on regroupe dans un tableau.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$DllImportConstructor = [System.Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))\n$FieldArray = [System.Reflection.FieldInfo[]] @(\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('SetLastError'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('CallingConvention'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('CharSet')\n)\n<\/pre>\n\n\n\n<p>On cr\u00e9er un second tableau qui aura pour vocation de porter les valeurs aux param\u00e8tres import\u00e9s.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$FieldValueArray = [Object[]] @(\n\t'MessageBoxW',\n\t$true,\n\t$true,\n\t[System.Runtime.InteropServices.CallingConvention]::Winapi,\n\t[System.Runtime.InteropServices.CharSet]::Unicode\n)<\/pre>\n\n\n\n<p>Puis on cr\u00e9er un attribut personnalis\u00e9 \u00e0 l\u2019aide de la classe <code>System.Reflection.Emit.CustomAttributeBuilder<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$SetLastErrorCustomAttribute = New-Object System.Reflection.Emit.CustomAttributeBuilder(\n\t$DLLImportConstructor,\n\t@('user32.dll'),\n\t$FieldArray,\n\t$FieldValueArray\n)<\/pre>\n\n\n\n<p>Enfin on ajoute l\u2019attribut \u00e0 notre m\u00e9thode et on cr\u00e9er le type pour que ce dernier soit accessible depuis PowerShell:<code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\"> $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)<\/code> puis <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$User32 = $TypeBuilder.CreateType()<\/code>. Si vous voulez un exemple un peu plus complexe, vous pouvez consulter <a rel=\"noreferrer noopener\" href=\"https:\/\/learn-powershell.net\/2016\/08\/28\/revisiting-netsession-function-using-psreflect\/\" data-type=\"URL\" data-id=\"https:\/\/learn-powershell.net\/2016\/08\/28\/revisiting-netsession-function-using-psreflect\/\" target=\"_blank\">ceci<\/a>. <\/p>\n\n\n\n<p>On peut \u00e9galement cr\u00e9er des \u00e9num\u00e9rations. En r\u00e9f\u00e9rence \u00e0 mon article sur le contournement de s\u00e9curit\u00e9 PowerShell, nous construirons une \u00e9num\u00e9ration qui contient les codes de retours de la fonction <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">AMSIScanContent()<\/code>. Pour cr\u00e9er notre \u00e9num\u00e9ration, il faut, comme pr\u00e9c\u00e9demment, cr\u00e9er un module en m\u00e9moire (ou la rajouter \u00e0 un d\u00e9j\u00e0 existant) et utiliser la variable <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$ModuleBuilder<\/code>, cette fois ci avec la m\u00e9thode <code>.DefineEnum()<\/code>. Elle prend en argument le nom de l\u2019\u00e9num\u00e9ration, puis les attributs de visibilit\u00e9 et enfin le type des \u00e9l\u00e9ments de l\u2019\u00e9num\u00e9ration. Pour ajouter une entr\u00e9e \u00e0 une \u00e9num\u00e9ration, on utilise la m\u00e9thode <code>.DefineLiteral()<\/code> qui prend en argument le nom du champ, puis sa valeur (attention, la m\u00e9thode \u00e0 un objet de retour, pour le masquer il faut le rediriger vers <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$null<\/code> ou <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[void]<\/code>). On a donc:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$TypeBuilder = $ModuleBuilder.DefineEnum(AMSI_RESULT, 'Public', [UInt32])\n$null = $TypeBuilder.DefineLiteral('AMSI_RESULT_CLEAN', [UInt32]0)\n$null = $TypeBuilder.DefineLiteral('AMSI_RESULT_NOT_DETECTED', [UInt32]1)\n$null = $TypeBuilder.DefineLiteral('AMSI_RESULT_BLOCKED_BY_ADMIN_START', [UInt32]16384)\n$null = $TypeBuilder.DefineLiteral('AMSI_RESULT_BLOCKED_BY_ADMIN_END', [UInt32]20479)\n$null = $TypeBuilder.DefineLiteral('AMSI_RESULT_DETECTED', [UInt32]32768)<\/pre>\n\n\n\n<p>On finit simplement par cr\u00e9er le type: <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$AMSI_RESULT = $TypeBuilder.CreateType()<\/code>. <\/p>\n\n\n\n<p>On peut enfin cr\u00e9er des structures. En r\u00e9f\u00e9rence \u00e0 mon article sur l\u2019abus d\u2019ACL en Active Directory, nous allons construire la structure du header d\u2019une ACL. Pour cr\u00e9er notre structure, il faut comme pr\u00e9c\u00e9demment, cr\u00e9er un module en m\u00e9moire (ou la rajouter \u00e0 un d\u00e9j\u00e0 existant) et utiliser la variable <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$ModuleBuilder<\/code>, mais avec la m\u00e9thode <code>.DefineType()<\/code> comme pour les fonctions. Il faut cependant ajouter plus d\u2019attributs. Certains sont constants <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$StructAttributes = [System.Reflection.TypeAttributes] 'Class, Public, Sealed, BeforeFieldInit'<\/code> d&#8217;autres non et sont en fonction des besoins (ils doivent \u00eatre rajout\u00e9s). Le type de disposition qui peut \u00eatre <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[System.Reflection.TypeAttribute]::SequencialLayout<\/code><\/code> ou bien <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[System.Reflection.TypeAttribute]::ExplicitLayout<\/code><\/code>. Le premier doit \u00eatre utilis\u00e9 dans les structures o\u00f9 il n\u2019y a pas d\u2019union pr\u00e9sent, le second le cas contraire. Le charset, qui peut prendre les valeurs suivantes <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[System.Reflection.TypeAttribute]::AnsiClass\/AutoClass\/UnicodeClass<\/code>. Pour ajouter des attributs \u00e0 notre variable, il faudra utiliser l\u2019op\u00e9rateur <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">-bor<\/code> de la mani\u00e8re suivante <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$StructAttributes = $StructAttributes -bor [System.Reflection.TypeAttributes]::attribut<\/code>. Le type sp\u00e9cifi\u00e9 devra \u00eatre <code>System.ValueType<\/code>. Pour ajouter un champ \u00e0 notre structure, il faudra alors utiliser la m\u00e9thode <code>.DefineField()<\/code> qui prend en argument le nom du champ, son type et enfin son attribut (attention, la m\u00e9thode \u00e0 un objet de retour, pour le masquer il faut le rediriger vers <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$null<\/code> ou <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[void]<\/code>). On a donc:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$TypeBuilder = $ModuleBuilder.DefineType('ACL', [System.Reflection.TypeAttribute], [System.ValueType])\n$null = $TypeBuilder.DefineField('AclRevision', [Byte], 'Public')\n$null = $TypeBuilder.DefineField('Sbz1', [Byte], 'Public')\n$null = $TypeBuilder.DefineField('AckSize', [UInt16], 'Public')\n$null = $TypeBuilder.DefineField('AceCount', [UInt16], 'Public')\n$null = $TypeBuilder.DefineField('Sbz2', [UInt16], 'Public')<\/pre>\n\n\n\n<p>On termine par la cr\u00e9ation de notre type: <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$ACL = $TypeBuilder.CreateType()<\/code>.<\/p>\n\n\n\n<p>Par ailleurs, si l\u2019on souhaite utiliser l\u2019attribut <code>MarshalAs()<\/code> sur l\u2019un des champs de notre structure, il faut commencer par obtenir un constructeur de l\u2019attribut, ce qui peut \u00eatre fait de 2 mani\u00e8res diff\u00e9rentes (la seconde \u00e9tant plus rapide que la premi\u00e8re):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"1\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructor(@([System.Runtime.InteropServices.UnmanagedType]))\n$ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0]<\/pre>\n\n\n\n<p>puis on cr\u00e9er un objet de la classe <code>System.Reflection.Emit.CustomAttributeBuilder<\/code>\u00a0:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$AttribBuilder = New-Object System.Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @([System.Runtime.InteropServices.UnmanagedType]::LPTStr))<\/pre>\n\n\n\n<p>ici, le type non manag\u00e9 est <code>LPTStr<\/code>, d\u2019autres peuvent \u00e9videmment \u00eatre utilis\u00e9. Simplement, le type doit appartenir \u00e0 l\u2019\u00e9num\u00e9ration <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[System.Runtime.InteropServices.UnmanagedType]<\/code>. Puis on sauvegarde le retour de la cr\u00e9ation d\u2019un champ et on lui applique la m\u00e9thode <code>.SetCustomAttribute()<\/code> avec comme param\u00e8tre, notre variable <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$AttribBuilder<\/code>\u00a0:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$newField = $TypeBuilder.DefineField('pName', [String], 'Public')\n$newField.SetCustomAttribute($AttribBuilder)<\/pre>\n\n\n\n<p>Bon c\u2019est clairement trop long en terme de quantit\u00e9 de code lorsqu\u2019un grand nombre d\u2019\u00e9num\u00e9ration\/structures\/fonctions est utilis\u00e9. PSReflect regroupe toutes ces \u00e9tapes dans plusieurs fonctions d\u00e9j\u00e0 nomm\u00e9es. On peut donc alors faire les m\u00eames impl\u00e9mentations en suivant les \u00e9tapes suivantes pour la fonction <code>MessageBox<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$InMemoryModule = New-InMemoryModule -ModuleName Win32Lib\n$Type = Add-Win32Type -Namespace Win32Lib -DllName User32 -FunctionName MessageBox -EntryPoint MessageBoxW -ReturnType ([Int32]) -ParameterTypes ([IntPtr], [String], [String], [Int32]) -SetLastError -Module $InMemoryModule<\/pre>\n\n\n\n<p>L\u2019\u00e9num\u00e9ration:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$AMSI_RESULT\u00a0=\u00a0psenum\u00a0$InMemoryModule\u00a0AMSI_RESULT\u00a0UInt32\u00a0@{\n\u00a0\u00a0\u00a0\u00a0AMSI_RESULT_CLEAN\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0=\u00a00\n\u00a0\u00a0\u00a0\u00a0AMSI_RESULT_NOT_DETECTED\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0=\u00a01\n\u00a0\u00a0\u00a0\u00a0AMSI_RESULT_BLOCKED_BY_ADMIN_START\u00a0=\u00a016384\n\u00a0\u00a0\u00a0\u00a0AMSI_RESULT_BLOCKED_BY_ADMIN_END\u00a0\u00a0\u00a0=\u00a020479\n\u00a0\u00a0\u00a0\u00a0AMSI_RESULT_DETECTED\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0=\u00a032768\n}<\/pre>\n\n\n\n<p>La structure:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ACL\u00a0=\u00a0struct\u00a0$InMemoryModule\u00a0ACL\u00a0@{\n\u00a0\u00a0\u00a0\u00a0AclRevision\u00a0=\u00a0field\u00a00\u00a0Byte\n\u00a0\u00a0\u00a0\u00a0Sbz1\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0=\u00a0field\u00a01\u00a0Byte\n\u00a0\u00a0\u00a0\u00a0AclSize\u00a0\u00a0\u00a0\u00a0\u00a0=\u00a0field\u00a02\u00a0UInt16\n\u00a0\u00a0\u00a0\u00a0AceCount\u00a0\u00a0\u00a0\u00a0=\u00a0field\u00a03\u00a0UInt16\n\u00a0\u00a0\u00a0\u00a0Sbz2\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0=\u00a0field\u00a04\u00a0UInt16\n}<\/pre>\n\n\n\n<p>Ce qui est vraiment plus rapide. De mani\u00e8re assez \u00e9vidente, si des structures en appellent d\u2019autres, il faudra simplement pr\u00e9senter la variable contenant le type en tant que type champ, et il en va de m\u00eame si le type concern\u00e9 se situe dans une \u00e9num\u00e9ration. Il existe un projet de <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/jaredcatkinson\" data-type=\"URL\" data-id=\"https:\/\/twitter.com\/jaredcatkinson\" target=\"_blank\">@Jared Atkinson<\/a>, <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/jaredcatkinson\/PSReflect-Functions\" data-type=\"URL\" data-id=\"https:\/\/github.com\/jaredcatkinson\/PSReflect-Functions\" target=\"_blank\">PSReflect-Functions<\/a> qui regroupe \u00e9norm\u00e9ment de fonctions\/\u00e9num\u00e9rations\/structures fr\u00e9quemment utilis\u00e9es. Ainsi lorsqu\u2019il faut utiliser du P\/Invoke avec PSReflect, le premier r\u00e9flexe \u00e0 s\u2019imposer est d\u2019aller voir si cette derni\u00e8re est d\u00e9j\u00e0 \u00e9crite, ce qui provoque naturellement un certain gain de temps. En revanche, comment impl\u00e9menter une fonction qui n\u2019est pas d\u00e9j\u00e0 pr\u00e9sente dans PSReflect-Functions. La premi\u00e8re \u00e9tape est d\u2019aller sur pinvoke.net et de chercher une signature pour sa fonction ou alors de lire la documentation officielle de Microsoft sur cette derni\u00e8re (sinon, il faut sortir les outils de r\u00e9tro-conception). Prenons la fonction <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">LookupPrivilegeValueA<\/code> de <code>advapi32.dll<\/code>. Voici la d\u00e9finition de cette derni\u00e8re:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">BOOL LookupPrivilegeValueA( LPCSTR lpSystemName, LPCSTR lpName, PLUID lpLuid );<\/pre>\n\n\n\n<p>On commence par \u00e9valuer le type de retour, sans trop de difficult\u00e9, ici ce sera <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[bool]<\/code>. Puis les types des arguments. Les deux premiers seront des <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[string]<\/code>, le dernier est un pointeur vers une structure. Pour cela il faut impl\u00e9menter ladite structure (premier r\u00e9flexe, PSReflect-Functions) puis on donne la variable de d\u00e9finition de la structure en tant que type de param\u00e8tre, enfin on ajoute <code>.MakeByRefType()<\/code> qui indique qu\u2019il s\u2019agit d\u2019un pointeur. Notre fonction ressemblera donc \u00e0 ceci:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">(func\u00a0advapi32\u00a0LookupPrivilegeValue\u00a0([bool])\u00a0@(\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0[String],\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0#\u00a0LPCSTR\u00a0lpSystemName\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0[String],\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0#\u00a0LPCSTR\u00a0lpName\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0$LUID.MakeByRefType()\u00a0#\u00a0PLUID\u00a0\u00a0lpLuid\n)\u00a0-EntryPoint\u00a0LookupPrivilegeValue\u00a0-SetLastError)<\/pre>\n\n\n\n<p>Pour s\u2019en servir, on fait comme cela:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$LUIDObject = [Activator]::CreateInstance($LUID) # on cr\u00e9er une structure de LUID\n$advapi32::LookupPrivilegeValue([String]::Empty, \"SeDebugPrivilege\", [ref]$LUIDObject)<\/pre>\n\n\n\n<p>Pour vous familiariser avec la programmation avec PSReflect je vous invite fortement \u00e0 lire le code de PSReflect-Functions.<\/p>\n\n\n\n<h2>SweetBackup<\/h2>\n\n\n\n<p>Lorsque l\u2019on souhaite utiliser certains droits, il est parfois n\u00e9cessaire d\u2019utiliser des fonctions de la WinAPI. C\u2019est le cas lorsque l\u2019on souhaite utiliser le droit <code>SeBackupPrivilege<\/code> qui doit absolument avoir un flag particulier lors de l\u2019appelle de la fonction <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">CreateFileA<\/code>. Or ce flag est tellement particulier, qu\u2019il n\u2019est accessible dans la tr\u00e8s grande majorit\u00e9 des utilitaires et des outils Windows. Les cmdlets comme <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Get-Item<\/code> ou <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Get-Content<\/code> ne l\u2019utilisent pas, les commandes comme <code>dir<\/code> ou <code>type<\/code> ne l\u2019impl\u00e9mentent pas. Seulement <code>robocopy.exe<\/code> (\u00e9ventuellement <code>xcopy.exe<\/code>) le fait. Or il est parfois tr\u00e8s utile de poss\u00e9der des outils permettant de l\u2019utiliser, par exemple dans le cas o\u00f9 un membre du groupe &#8220;Backup Operators&#8221; a \u00e9t\u00e9 compromis. Jusqu\u2019alors, la seule possibilit\u00e9 \u00e9tait de charger <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/giuliano108\/SeBackupPrivilege\" data-type=\"URL\" data-id=\"https:\/\/github.com\/giuliano108\/SeBackupPrivilege\" target=\"_blank\">en m\u00e9moire deux dll .NET<\/a> mais vous le savez, d\u00e9poser des fichiers, c\u2019est laisser des traces. Mon objectif \u00e9tait d\u2019obtenir une impl\u00e9mentation en PowerShell qui s\u2019affranchissait de cette restriction. Seulement, il est n\u00e9cessaire de faire du P\/Invoke pour utiliser le fameux flag. Mon choix s\u2019est alors naturellement tourn\u00e9 vers PSReflect. J\u2019ai donc besoin de:<\/p>\n\n\n\n<ul><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">RtlAdjustPrivilege<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">CreateFile<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">ReadFile<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">WriteFile<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">LookupPrivilegeDisplayName<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">LookupPrivilegeName<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">GetTokenInformation<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">OpenProcessToken<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">CloseHandle<\/code><\/li><li><code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">GetCurrentProcess<\/code><\/li><\/ul>\n\n\n\n<p>Dans le but de cr\u00e9er les fonctions cmdlets suivantes:<\/p>\n\n\n\n<ul><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Get-SeBackupPrivilege<\/code> qui permet de savoir si le droit <code>SeBackupPrivilege<\/code> est activ\u00e9 ou non.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Set-SeBackupPrivilege<\/code> qui permet de changer le statut du droit <code>SeBackupPrivilege<\/code>.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Read-FileContent<\/code> qui permet de lire le contenu d\u2019un fichier.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Set-FileContent<\/code> qui permet de changer le contenu d\u2019un fichier.<\/li><li><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Copy-File<\/code> qui permet de copier un fichier.<\/li><\/ul>\n\n\n\n<p>Je ne parlerais pas de la programmation des fonctions en elles-m\u00eames car ce n\u2019est pas tr\u00e8s int\u00e9ressant. En revanche je vais me concentrer sur la partie P\/Invoke. Il faut savoir que certaines de ces fonctions sont d\u00e9j\u00e0 impl\u00e9menter dans la librairie PSReflect-Functions, je me suis donc all\u00e8grement servis (pourquoi s\u2019en priver apr\u00e8s tout). Il me restait alors qu\u2019\u00e0 impl\u00e9menter <code>Read\/Write<\/code>File. La premi\u00e8re est d\u00e9finie ainsi:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">BOOL ReadFile( HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped );<\/pre>\n\n\n\n<p>D\u2019apr\u00e8s le tableau de tout \u00e0 l\u2019heure, <code>hFile<\/code> sera un <code>IntPtr<\/code>,<code> nNumberOfBytesToRead<\/code> est un <code>UInt32<\/code>, <code>lpNumverOfBytesRead<\/code> un pointeur vers un <code>UInt32<\/code>. D\u2019apr\u00e8s la documentation de cette m\u00eame fonction, le dernier param\u00e8tre correspond \u00e0 une structure particuli\u00e8re qui doit \u00eatre utilis\u00e9 dans le cas o\u00f9 le <code>HANDLE<\/code> vers notre fichier a \u00e9t\u00e9 cr\u00e9\u00e9 avec le flag <code>FILE_FLAG_OVERLAPPED<\/code>, ce qui n\u2019est guerre notre cas. On peut donc se permettre d\u2019utiliser un <code>IntPtr<\/code> qui sera vraisemblablement nul. Le type <code>LPVOID<\/code> n\u2019a pas de clair \u00e9quivalent, il d\u00e9pend de la situation dans lequel il est utilis\u00e9. En regardant sur pinvoke.net pour la signature de <code>ReadFile<\/code>, il semblerait que ce soit un tableau <code>Byte<\/code> qui doit \u00eatre utilis\u00e9. Notre d\u00e9finition ressemblera donc \u00e0 cela:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">(func kernel32 ReadFile ([bool]) @(\n        [IntPtr],                 # HANDLE       hFile\n        [Byte[]],                 # LPVOID       lpBuffer\n        [UInt32],                 # DWORD        nNumberOfBytesToRead\n        [UInt32].MakeByRefType(), # LPDWORD      lpNumberOfBytesRead\n        [IntPtr]                  # LPOVERLAPPED lpOverlapped\n) -EntryPoint ReadFile -SetLastError)<\/pre>\n\n\n\n<p>La seconde fonction est d\u00e9finie ainsi:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">BOOL WriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped );<\/pre>\n\n\n\n<p>Les types des param\u00e8tres deviennent alors \u00e9vident, <code>hFile<\/code> est un <code>IntPtr<\/code>, <code>lpBuffer<\/code> un <code>Byte[]<\/code>, <code>nNumberOfBytesToWrite<\/code> est un <code>UInt32<\/code>, <code>lpNumberOfBytesWritten<\/code> est un pointeur vers un <code>UInt32<\/code>, <code>lpOverlapped<\/code> est un <code>IntPtr<\/code> qui sera vraisemblablement nul. Notre d\u00e9finition ressemblera donc \u00e0 cela:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">(func kernel32 WriteFile ([bool]) @(\n        [IntPtr],                 # HANDLE       hFile\n        [Byte[]],                 # LPCVOID      lpBuffer\n        [UInt32],                 # DWORD        nNumberOfBytesToWrite\n        [UInt32].MakeByRefType(), # LPDWORD      lpNumberOfBytesWritten\n        [IntPtr]                  # LPOVERLAPPED lpOverlapped\n) -EntryPoint WriteFile -SetLastError)<\/pre>\n\n\n\n<p>Or l\u2019ensemble de ces fonctions n\u00e9cessite un certain nombre d\u2019\u00e9num\u00e9rations et de structures que voici:<\/p>\n\n\n\n<ul><li><code>TOKEN_INFORMATION_CLASS<\/code><\/li><li><code>FILE_ACCESS<\/code><\/li><li><code>FILE_SHARE<\/code><\/li><li><code>CREATION_DISPOSITION<\/code><\/li><li><code>FILE_FLAGS_AND_ATTRIBUTES<\/code><\/li><li><code>TOKEN_ACCESS<\/code><\/li><li><code>SE_PRIVILEGE<\/code><\/li><li><code>SecurityEntity<\/code><\/li><li><code>TOKEN_PRIVILEGES<\/code> et ses compl\u00e9ments<\/li><\/ul>\n\n\n\n<p>Qui sont par chance d\u00e9j\u00e0 d\u00e9finies dans PSReflect-Functions. On commence alors par cr\u00e9er notre module:<code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\"> $Module = New-InMemoryModule -ModuleName BackupMode<\/code> Puis on ajoute nos \u00e9num\u00e9rations et nos structures. Ensuite on cr\u00e9e une variable <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$FunctionDefinition<\/code> qui est un tableau contenant toutes nos fonctions. On ajoute alors le type: <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$Types = $FunctionDefinition | Add-Win32Type -Module $Module -Namespace BackupMode<\/code> Enfin, on r\u00e9cup\u00e8re chaque dll et nous stockons son objet dans une variable pour rendre plus ais\u00e9 l\u2019utilisation de ses fonctions:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$advapi32 = $Types['advapi32']\n$kernel32 = $Types['kernel32']\n$ntdll = $Types['ntdll']<\/pre>\n\n\n\n<p>On peut alors acc\u00e9der aux fonctions par leur variable comme ceci, en prenant l\u2019exemple de la fonction <code>GetCurrentProcess<\/code> qui retourne un <code>HANDLE<\/code> pour notre processus (qui sera d\u2019ailleurs -1 tout le temps, mais il est pr\u00e9f\u00e9rable, en accord avec la documentation Microsoft, d\u2019utiliser cette fonction plut\u00f4t que d\u2019utiliser une valeur cod\u00e9e en dure): <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$kernel32::GetCurrentProcess()<\/code><\/p>\n\n\n\n<h2>CVE-2021-1675<\/h2>\n\n\n\n<p>CVE-2021-1675 est une LPE\/RCE utilisant le tristement c\u00e9l\u00e8bre <code>PrintSpooler<\/code> qui permet de g\u00e9rer les syst\u00e8mes d\u2019impressions chez Windows. L\u2019exploitation de la vuln\u00e9rabilit\u00e9 PrintNightmare est un bon exemple pour utiliser le Pinvoke. Dans cette partie, nous allons voir comment utiliser ces m\u00e9thodes de Pinvoke manuelle pour construire notre exploit (je me suis beaucoup inspir\u00e9 du travail de <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/cube0x0\" data-type=\"URL\" data-id=\"https:\/\/twitter.com\/cube0x0\" target=\"_blank\">@cube0x0<\/a>, de <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/calebjstewart\" data-type=\"URL\" data-id=\"https:\/\/twitter.com\/calebjstewart\" target=\"_blank\">@CalebStewart<\/a> et <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/_johnhammond\" data-type=\"URL\" data-id=\"https:\/\/twitter.com\/_johnhammond\" target=\"_blank\">@JohnHammond<\/a>). Nous commen\u00e7ons par cr\u00e9er un module en m\u00e9moire:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$DynAssembly = New-Object System.Reflection.AssemblyName('BlueNightmareAssembly')\n$AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)\n$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('BlueNightmare', $False)<\/pre>\n\n\n\n<p>Cet exploit requi\u00e8re l\u2019utilisation de deux fonctions de <code>winspool.drv<\/code>, <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">AddPrinterDriverEx<\/code> et <code data-enlighter-language=\"cpp\" class=\"EnlighterJSRAW\">EnumPrinterDrivers<\/code> dont je vous invite \u00e0 consulter la documentation pour voir les types des diff\u00e9rents param\u00e8tres. On commence donc par d\u00e9finir les invariants de d\u00e9finition des fonctions, ainsi que le type \u00e0 ajouter dans le module:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$TypeBuilder = $ModuleBuilder.DefineType('winsplool', 'Public, Class')\n\n$DllImportConstructor = [System.Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))\n$FieldArray = [System.Reflection.FieldInfo[]] @(\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('SetLastError'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('CallingConvention'),\n\t[System.Runtime.InteropServices.DllImportAttribute].GetField('CharSet')\n)<\/pre>\n\n\n\n<p>Ce qui nous permet d\u2019ajouter la premi\u00e8re fonction, dont le type de retour est un bool\u00e9en:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$PInvokeMethod = $TypeBuilder.DefineMethod(\n\t'AddPrinterDriverEx',\n\t[Reflection.MethodAttributes] 'Public, Static',\n\t[Bool],\n\t[Type[]] @([String], [UInt32], [IntPtr], [UInt32])\n)\n\n$FieldValueArray = [Object[]] @(\n\t'AddPrinterDriverEx',\n\t$true,\n\t$true,\n\t[System.Runtime.InteropServices.CallingConvention]::Winapi,\n\t[System.Runtime.InteropServices.CharSet]::Unicode\n)\n\n$SetLastErrorCustomAttribute = New-Object System.Reflection.Emit.CustomAttributeBuilder(\n\t$DLLImportConstructor,\n\t@('winspool.drv'),\n\t$FieldArray,\n\t$FieldValueArray\n)\n\n$PinvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)<\/pre>\n\n\n\n<p>Puis on fait de m\u00eame avec la seconde fonction:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$PInvokeMethod = $TypeBuilder.DefineMethod(\n\t'EnumPrinterDrivers',\n\t[Reflection.MethodAttributes] 'Public, Static',\n\t[Bool],\n\t[Type[]] @([String], [String], [UInt32], [IntPtr], [UInt32], [UInt32].MakeByRefType(), [UInt32].MakeByRefType())\n)\n\n$FieldValueArray = [Object[]] @(\n\t'EnumPrinterDrivers',\n\t$true,\n\t$true,\n\t[System.Runtime.InteropServices.CallingConvention]::Winapi,\n\t[System.Runtime.InteropServices.CharSet]::Auto\n)\n\n$SetLastErrorCustomAttribute = New-Object System.Reflection.Emit.CustomAttributeBuilder(\n\t$DLLImportConstructor,\n\t@('winspool.drv'),\n\t$FieldArray,\n\t$FieldValueArray\n)\n\n$PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)<\/pre>\n\n\n\n<p>On finit par cr\u00e9er le type qui contiendra les fonctions avec <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$winspool = $TypeBuilder.CreateType()<\/code>. La derni\u00e8re fonction import\u00e9e doit nous obtenir des informations qui devront se situer dans la structure <code>DRIVER_INFO_2<\/code> que nous devons donc d\u00e9finir. D\u2019apr\u00e8s pinvoke.net tout les champs qui sont des string doivent \u00eatre marshall\u00e9 vers le type <code>LPTStr<\/code>. En reprenant les \u00e9tapes vues plus haut:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructor(@([System.Runtime.InteropServices.UnmanagedType]))\n$AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @([System.Runtime.InteropServices.UnmanagedType]::LPTStr))\n\n$TypeBuilder = $ModuleBuilder.DefineType('_DRIVER_INFO_2', 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit', [System.ValueType], [Reflection.Emit.PackingSize]::Unspecified)\n\n$null = $TypeBuilder.DefineField('cVersion', [UInt32], 'Public')\n\n$newField = $TypeBuilder.DefineField('pName', [String], 'Public')\n$newField.SetCustomAttribute($AttribBuilder)\n\n$newField = $TypeBuilder.DefineField('pEnvironment', [String], 'Public')\n$newField.SetCustomAttribute($AttribBuilder)\n\n$newField = $TypeBuilder.DefineField('pDriverPath', [String], 'Public')\n$newField.SetCustomAttribute($AttribBuilder)\n\n$newField = $TypeBuilder.DefineField('pDataFile', [String], 'Public')\n$newField.SetCustomAttribute($AttribBuilder)\n\n$newField = $TypeBuilder.DefineField('pConfigFile', [String], 'Public')\n$newField.SetCustomAttribute($AttribBuilder)\n\n\n$DRIVER_INFO_2 = $TypeBuilder.CreateType()<\/pre>\n\n\n\n<p>Et nous avons termin\u00e9 pour le Pinvoke, je vous invite tout de m\u00eame \u00e0 lire le reste du programme pour vous faire une id\u00e9e. Selon moi, il \u00e9tait pr\u00e9f\u00e9rable dans cette situation d\u2019utiliser manuellement le Pinvoke, car l\u2019exploit n\u00e9cessitait tr\u00e8s peu d\u2019importation, et par cons\u00e9quent, les 650 lignes de PSReflect en plus n\u2019auraient pas \u00e9t\u00e9 n\u00e9cessaire. Le r\u00e9sultat, environ 500 lignes \u00e9conomis\u00e9es. \u00c9videmment, on peut utiliser PSReflect pour faire le m\u00eame travail, mais cela me semble un peut moins pertinent, contrairement \u00e0 SweetBackup o\u00f9 beaucoup d\u2019importations sont faites, et donc nous \u00e9conomisons des lignes et du travail.<\/p>\n\n\n\n<h2>Bonnes pratiques de programmation<\/h2>\n\n\n\n<p>Dans cette section nous allons parler de bonnes pratiques \u00e0 prendre dans le but d\u2019avoir un code plus lisible, mais \u00e9galement d&#8217;assurer la compatibilit\u00e9 avec toutes les versions de PowerShell.<\/p>\n\n\n\n<p>Pour les fonctions, il est d\u2019usage d\u2019indiquer apr\u00e8s un param\u00e8tre, en commentaire, le type et le nom du param\u00e8tre conform\u00e9ment \u00e0 la documentation de Microsoft. Ainsi, il devient plus ais\u00e9 de comprendre pourquoi quel type. Cependant, d\u00e9pendant de l\u2019usage qui est fait de la fonction native, un param\u00e8tre n\u2019a guerre l\u2019obligation d\u2019\u00eatre du bon type. En effet, comme vous le verrez juste apr\u00e8s, si un param\u00e8tre est d\u00e9di\u00e9e \u00e0 une certaine op\u00e9ration pr\u00e9cise qui ne sera jamais accomplie, on peut le remplacer par le type <code>IntPtr<\/code> et le sp\u00e9cifi\u00e9 \u00e0 <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[IntPtr]::Zero<\/code> lors de l\u2019appel. Pour g\u00e9n\u00e9raliser cette remarque, lorsqu\u2019un param\u00e8tre sera nul lors de l\u2019appel dans tout les cas de son utilisation, il est mieux de remplacer ce dernier par un <code>IntPtr<\/code> qui sera toujours <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[IntPtr]::Zero<\/code>. Dans le but d\u2019assurer une bonne compatibilit\u00e9 de code, il est toujours pr\u00e9f\u00e9rable de ne pas appeler les fonctions charg\u00e9es via l&#8217;espace de nom cr\u00e9\u00e9 par le module en m\u00e9moire, mais plut\u00f4t par leur variable comme ci dessus. En effet PowerShell v2 est un peu ennuyant sur cet aspect.<\/p>\n\n\n\n<p>Concernant les structures, de mani\u00e8re g\u00e9n\u00e9rale, puisque les structures sont des types dynamiques, les m\u00e9thodes de la r\u00e9flexion s&#8217;utilisent pour les instancier. Il faut \u00e9galement savoir que les structures g\u00e9n\u00e9r\u00e9es par PSReflect sont fournies avec une m\u00e9thode <code>.GetSize()<\/code> qui permet d\u2019\u00e9viter de tout le temps utiliser <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">[System.Runtime.InteropServices.Marshal]::SizeOf()<\/code>. Il est \u00e9galement pr\u00e9f\u00e9rable, pour un code clair, d\u2019aligner les \u00e9l\u00e9ments, au niveau du symbole &#8220;=&#8221;, lors de la d\u00e9finition de la structure, pour qu\u2019il soit plus agr\u00e9able de la lire. Le cas des unions est un peu particulier. Pour rappel, lorsqu\u2019une structure contient un union, cela signifie qu\u2019un m\u00eame champ peut prendre de type diff\u00e9rent mais pas les deux. Prenons exemple de la structure <code>IO_STATUS_BLOCK<\/code>\u00a0:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">typedef struct _IO_STATUS_BLOCK {\n  union {\n    NTSTATUS Status;\n    PVOID    Pointer;\n  };\n  ULONG_PTR Information;\n} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;<\/pre>\n\n\n\n<p>Lors de l\u2019utilisation de cette structure, le premier offset (donc 0), soit il contient un <code>UInt32<\/code> (<code>NTSTATUS<\/code>) soit un pointeur mais pas les deux en m\u00eame temps. Il est \u00e9videmment possible de r\u00e9aliser de tels choses en C# il en d\u00e9coule naturellement que PowerShell est \u00e9galement apte \u00e0 le faire. Pour cela, on doit changer le type de structure \u00e0 cr\u00e9er en ajoutant le param\u00e8tre <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">-ExplicitLayout<\/code><\/code> \u00e0 la fonction <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">struct<\/code>, et en pr\u00e9cisant l\u2019offset o\u00f9 vit chaque valeur. Ainsi nous devrions obtenir avec PSReflect la structure suivante pour <code>IO_STATUS_BLOCK<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$IO_STATUS_BLOCK = struct $STRUCT IO_STATUS_BLOCK @{\n    Status  = field 0 Int64  -Offset 0\n    Pointer = field 1 IntPtr -Offset 0\n} -ExplicitLayout<\/pre>\n\n\n\n<p>Si les param\u00e8tres de la structure doivent \u00eatre marshall\u00e9es, ils faut utiliser le flag <code>-MarshalAs<\/code> avec une liste contenant le type de destination, et \u00e9ventuellement sa taille. Pour reprendre l&#8217;exemple en PInvoke manuel: <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">pName = field 0 string -MarshalAs @(\"LPTStr\")<\/code><\/p>\n\n\n\n<p>Il faut voir les \u00e9num\u00e9rations comme des classes o\u00f9 les m\u00e9thodes sont les valeurs de l\u2019\u00e9num\u00e9ration. Ainsi, on utilise\u00a0<code>::<\/code> apr\u00e8s la variable de l\u2019\u00e9num\u00e9ration. Il est \u00e0 noter que lorsque l\u2019on acc\u00e8de \u00e0 une valeur d\u2019une \u00e9num\u00e9ration, la valeur qui sera retourn\u00e9e sera le nom de la valeur plut\u00f4t que la d\u00e9finition de celle-ci. Pour y acc\u00e9der, il faudra utiliser la propri\u00e9t\u00e9 <code>value__<\/code>, mais quand on doit utiliser une \u00e9num\u00e9ration, il ne faut pas s\u2019inqui\u00e9ter et appeler la propri\u00e9t\u00e9 derni\u00e8rement cit\u00e9, simplement le nom de la valeur souhait\u00e9. Tout comme les structures, il faut aussi penser \u00e0 bien aligner les valeurs au niveau du &#8220;=&#8221; pour plus de clart\u00e9.<\/p>\n\n\n\n<p>Une derni\u00e8re chose, PSReflect est tr\u00e8s utile, mais gardez \u00e0 l\u2019esprit que s\u2019il faut simplement importer une seule fonction, il est pr\u00e9f\u00e9rable d\u2019\u00e9conomiser du code et utiliser le Pinvoke manuellement.<\/p>\n\n\n\n<p>J\u2019esp\u00e8re que cet article vous aura plu et vous donne une meilleur compr\u00e9hension de PSReflect et du P\/Invoke en PowerShell\u00a0! En revanche, je n\u2019ai pas trait\u00e9 de toutes les techniques possibles pour faire du Pinvoke en PowerShell, mais si l\u2019anglais ne vous effraie pas, je vous invite \u00e0 consulter les documents de la formation <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/specterops\/at-ps\" data-type=\"URL\" data-id=\"https:\/\/github.com\/specterops\/at-ps\" target=\"_blank\">Adversary Tactics PowerShell de SpecterOps<\/a> qui y d\u00e9die une partie enti\u00e8re. SweetBackup est disponible <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/rootSySdk\/SweetBackup\" data-type=\"URL\" data-id=\"https:\/\/github.com\/rootSySdk\/SweetBackup\" target=\"_blank\">ici<\/a> et l&#8217;exploit pour PrintNightmare <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/rootSySdk\/PowerShellSample\" data-type=\"URL\" data-id=\"https:\/\/github.com\/rootSySdk\/PowerShellSample\" target=\"_blank\">ici<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Bien que les possibilit\u00e9s offertes par .NET sont extr\u00eamement puissantes, il est parfois n\u00e9cessaire d\u2019avoir une interaction directe avec la WinAPI. En C#, le Pinvoke y est d\u00e9di\u00e9. Dans cet article, nous verrons comment faire une impl\u00e9mentation discr\u00e8te, stable et viable du Pinvoke. L\u2019exemple principal de ce poste sera celui d\u2019un outil que j\u2019ai \u00e9crit, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[],"_links":{"self":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/224"}],"collection":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/comments?post=224"}],"version-history":[{"count":11,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/224\/revisions"}],"predecessor-version":[{"id":245,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/224\/revisions\/245"}],"wp:attachment":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/media?parent=224"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/categories?post=224"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/tags?post=224"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}