Using Powershell and Reflection API to Invoke Methods from .NET
Total Page:16
File Type:pdf, Size:1020Kb
Using Powershell and Reflection API to invoke methods from .NET Assemblies written by Khai Tran | October 14, 2013 During application assessments, I have stumbled upon several cases when I need to call out a specific function embedded in a .NET assembly (be it .exe or .dll extension). For example, an encrypted database password is found in a configuration file. Using .NET Decompiler, I am able to see and identify the function used to encrypt the database password. The encryption key appears to be static, so if I could call the corresponding decrypt function, I would be able to recover that password. Classic solution: using Visual Studio to create new project, import encryption library, call out that function if it’s public or use .NET Reflection API if it’s private (or just copy the class to the new workspace, change method accessibility modifier to public and call out the function too if it is self-contained). Alternative (and hopeful less-time consuming) solution: Powershell could be used in conjunction with .NET Reflection API to invoke methods directly from the imported assemblies, bypassing the need of an IDE and the grueling process of compiling source code. Requirements Powershell and .NET framework, available at http://www.microsoft.com/en-us/download/details.aspx?id=34595 Note that Powershell version 3 is used in the below examples, and the assembly is developed in C#. Walkthrough First, identify the fully qualified class name (typically in the form of Namespace.Classname ), method name, accessibility level, member modifier and method arguments. This can easily be done with any available .NET Decompiler (dotPeek, JustDecompile, Reflector) Scenario 1: Public static class – Call public static method namespace AesSample { public class AesLibStatic { ... public static string DecryptString(string cipherText) { return DecryptStringPrivate(StringToByteArray(cipherText)); } This is the vanilla case, essentially in powershell you just need to call [Namespace].[Classname]::(params[]) And it only took 2 lines of code to do it: Load all .NET binaries in the folder Get-ChildItem -recurse "D:DocumentsVisual Studio 2010ProjectsAesSampleAesSamplebinDebug"|Where-Object {($_.Extension -EQ ".dll") -or ($_.Extension -eq ".exe")} | ForEach-Object { $AssemblyName=$_.FullName; Try {[Reflection.Assembly]::LoadFile($AssemblyName)} Catch{ "***ERROR*** Not .NET assembly: " + $AssemblyName}} #Call public static method [AesSample.AesLibStatic]::DecryptString("8E3C5A3088CEA26B634CF DA09D13A7DB") Result: Scenario 2: Public static class – Call private static method Let’s say you want to call this private static method, assuming the method name is unique within the class private static string DecryptStringSecret(string cipherText) { return DecryptStringPrivate(StringToByteArray(cipherText)); } Private methods can’t be accessed directly from Powershell object, instead you will need to find it by name and correct binding flags. More information about binding flags could be found here: http://msdn.microsoft.com/en-us/library/4ek9c21e.aspx #Load all .NET binaries in the folder Get-ChildItem -recurse "D:DocumentsVisual Studio 2010ProjectsAesSampleAesSamplebinDebug"|Where-Object {($_.Extension -EQ ".dll") -or ($_.Extension -eq ".exe")} | ForEach-Object { $AssemblyName=$_.FullName; Try {[Reflection.Assembly]::LoadFile($AssemblyName)} Catch{ "***ERROR*** Not .NET assembly: " + $AssemblyName}} #Only retrieve static private method $BindingFlags= [Reflection.BindingFlags] "NonPublic,Static" #Load method based on name $PrivateMethod = [AesSample.AesLibStatic].GetMethod("DecryptStringSecret",$bind ingFlags) #Invoke $PrivateMethod.Invoke($null,"8E3C5A3088CEA26B634CFDA09D13A7DB" ) Scenario 2 Extension: Function Overloading: Public static class – Call private static method In some cases, programmer takes advantage of function overloading feature of Object-Oriented languages – i.e multiple methods can have the same name as long as they have different argument list. For example: private static string DecryptStringPrivate(string cipherText) { return DecryptStringFromBytes_Aes(StringToByteArray(cipherText), key, iv); } private static string DecryptStringPrivate(byte[] cipherText) { return DecryptStringFromBytes_Aes(cipherText, key, iv); } Note that the two DecryptStringPrivate methods have the same name, but one takes a string as input, while another takes a bytearray as input. In this case, to look up the right method, you will need method name and method signature. The snippet below will invoke DecryptStringPrivate(byte[] cipherText) #Load all .NET binaries in the folder Get-ChildItem -recurse "D:DocumentsVisual Studio 2010ProjectsAesSampleAesSamplebinDebug"|Where-Object {($_.Extension -EQ ".dll") -or ($_.Extension -eq ".exe")} | ForEach-Object { $AssemblyName=$_.FullName; Try {[Reflection.Assembly]::LoadFile($AssemblyName)} Catch{ "***ERROR*** Not .NET assembly: " + $AssemblyName}} #Search for private method based on name $PrivateMethods = [AesSample.AesLibStatic].GetMethods($bindingFlags) | Where- Object Name -eq DecryptStringPrivate $PrivateMethods | ForEach-Object{ $PrivateMethod=$_ $MethodParams=$PrivateMethod.GetParameters() $MemberSignature = $MethodParams | Select -First 1 | Select- Object Member #This will list all the method signatures $MemberSignature.Member.ToString() #Choose the correct method based on parameter list If ($MemberSignature.Member.ToString() -eq "System.String DecryptStringPrivate(Byte[])"){ [byte[]]$Bytes =@(70,1,65,70,155,197,95,238,85,79,190,34,158,69,125,233,53,21 2,111,19,248,209,147,180,19,172,150,25,97,41,127,175) [Object[]] $Params=@(,$Bytes) #Call with the right arguments $PrivateMethod.Invoke($null,$Params) } } Scenario 3: Public class – Call nonstatic public method If a class is not declared with “static” keyword, its methods can’t be invoked directly from the class itself but from an instance of the class with the following snippet: Classname a = new Classname(); a.methodName(args[]); For example: namespace AesSample { public class AesLib {...public string DecryptString(string cipherText) { return DecryptStringPrivate(StringToByteArray(cipherText)); } Sample solution: #Load all .NET binaries in the folder Get-ChildItem -recurse "D:DocumentsVisual Studio 2010ProjectsAesSampleAesSamplebinDebug"|Where-Object {($_.Extension -EQ ".dll") -or ($_.Extension -eq ".exe")} | ForEach-Object { $AssemblyName=$_.FullName; Try {[Reflection.Assembly]::LoadFile($AssemblyName)} Catch{ "***ERROR*** Not .NET assembly: " + $AssemblyName}} #Call default constructor (no argument) $AesSample= New-Object "AesSample.AesLib" #Call constructor with arguments using this syntax: $AesSample= New-Object "AesSample.AesLib" ("a","b") #Invoke public method $AesSample.DecryptString("8E3C5A3088CEA26B634CFDA09D13A7DB") Scenario 4: Public class: Function Overloading – Call nonstatic private method This is very similar to Scenario 2: extension above. Again you will need both method name and argument list to call the right method. private string DecryptStringPrivate(string cipherText) { return DecryptStringFromBytes_Aes(StringToByteArray(cipherText), key, iv); } private string DecryptStringPrivate(byte[] cipherText) { return DecryptStringFromBytes_Aes(cipherText, key, iv); } Solution: #Load all .NET binaries in the folder Get-ChildItem -recurse "D:DocumentsVisual Studio 2010ProjectsAesSampleAesSamplebinDebug"|Where-Object {($_.Extension -EQ ".dll") -or ($_.Extension -eq ".exe")} | ForEach-Object { $AssemblyName=$_.FullName; Try {[Reflection.Assembly]::LoadFile($AssemblyName)} Catch{ "***ERROR*** Not .NET assembly: " + $AssemblyName}} #Call constructor $Instance= New-Object "AesSample.AesLib" ("a","b") # Find private nonstatic method. If you want to invoke static private method, replace Instance with Static $BindingFlags= [Reflection.BindingFlags] "NonPublic,Instance" $Instance.GetType().GetMethods($BindingFlags) | Where-Object Name -eq DecryptStringPrivate| ForEach-Object{ $PrivateMethod=$_ $MethodParams=$PrivateMethod.GetParameters() $MemberSignature = $MethodParams | Select -First 1 | Select- Object Member $MemberSignature.Member.ToString() If ($MemberSignature.Member.ToString() -eq "System.String DecryptStringPrivate(Byte[])"){ [byte[]]$Bytes =@(70,1,65,70,155,197,95,238,85,79,190,34,158,69,125,233,53,21 2,111,19,248,209,147,180,19,172,150,25,97,41,127,175) [Object[]] $Params=@(,$Bytes) # You will need to pass the Instance here instead of $null $PrivateMethod.Invoke($Instance,$Params) } } Closing thoughts: I didn’t include code to call out methods from private class in this post. Mainly because usually you can find a public class that reference to private class if it needs to use some methods of the private class, and then you can just invoke the calling method of the public class instead. Those snippets work under assumption that all necessary .NET assemblies are located in the same folder. If other externally-linked .NET assemblies are required, add additional code to load them into memory. Same with externally-linked native assemblies: either set them in your PATH environment variable, manually copy them to C:Windowssystem32 (not recommended) or load them with Powershell’s DllImport: http://blogs.msdn.com/b/mattbie/archive/2010/02/23/how-t o-call-net-and-win32-methods-from-powershell-and-your- troubleshooting-packs.aspx This method may also be useful in situations where you can’t decompile the application’s assemblies due to legal constraints. Consult with client or your contact before doing this, but it may be OK to list assembly’s methods and call them when