My problem is in calling shellcode in C#. I alloc memory in the process, write there my bytes and try to call it with Marshal.GetDelegateFromFunctionPointer(). It seems working, but, only if my function doesn't have parameters. If there's any parameter there then it's value equals {Unable to read memory}. In C++ I just need to typedef func, cast it, and call. How to do this properly in C#? (Shellcode performs jump to another C# func)
1 Answer
First of all in C# you have to declare delegate with all required parameters and calling convension. When you use GetDelegateForFunctionPointer ptr is converted to a delegate that invokes the unmanaged method using the __stdcall calling convention on Windows, or the __cdecl calling convention on Linux and macOS. You can set the calling convention by applying the UnmanagedFunctionPointerAttribute to the delegate. Also you need indicate how to marshal parameters between managed and unmanaged code to achieve this you should use MarshalAsAttribute
See remarks section of Marshal.GetDelegateForFunctionPointer
and UnmanagedFunctionPointerAttribute
Example:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern IntPtr LoadLibraryW([MarshalAs(UnmanagedType.LPWStr)] string libFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
const int MEM_COMMIT = 0x00001000;
const int PAGE_READWRITE = 0x04;
const int PAGE_EXECUTE_READ = 0x20;
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr VirtualAlloc(IntPtr address, IntPtr size, int allocationType, int protect);
[DllImport("kernel32.dll", ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool VirtualProtect(IntPtr address, IntPtr size, int newProtect, out int oldProtect);
private delegate int GetTempPath(int nBufferLength, [MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer);
static void Main(string[] args)
{
IntPtr hModule = LoadLibraryW("c:\\windows\\system32\\kernel32.dll");
IntPtr procAddress = GetProcAddress(hModule, "GetTempPathA");
// allocate page with read/write access
IntPtr page = VirtualAlloc(IntPtr.Zero, new IntPtr(4096), MEM_COMMIT, PAGE_READWRITE);
// write to allocated memory
// mov r11, procAddress
Marshal.WriteByte(page, 0, 0x49);
Marshal.WriteByte(page, 1, 0xBB);
Marshal.WriteInt64(page, 2, procAddress.ToInt64());
// jmp r11
Marshal.WriteByte(page, 0xA, 0x41);
Marshal.WriteByte(page, 0xB, 0xFF);
Marshal.WriteByte(page, 0xC, 0xE3);
// protect memory to allow execute code
bool result = VirtualProtect(page, new IntPtr(4096), PAGE_EXECUTE_READ, out int oldProtect);
var getTempPathTrampoline = Marshal.GetDelegateForFunctionPointer<GetTempPath>(page);
StringBuilder builder = new StringBuilder(1024);
getTempPathTrampoline(1024, builder);
Console.WriteLine(builder.ToString());
}
Example (call managed code)
static void Main(string[] args)
{
GetTempPath delegateToManaged = ManagedMethod;
IntPtr managedPtr = Marshal.GetFunctionPointerForDelegate(delegateToManaged);
// allocate page with read/write access
IntPtr page = VirtualAlloc(IntPtr.Zero, new IntPtr(4096), MEM_COMMIT, PAGE_READWRITE);
// write to allocated memory
// mov r11, procAddress
Marshal.WriteByte(page, 0, 0x49);
Marshal.WriteByte(page, 1, 0xBB);
Marshal.WriteInt64(page, 2, managedPtr.ToInt64());
// jmp r11
Marshal.WriteByte(page, 0xA, 0x41);
Marshal.WriteByte(page, 0xB, 0xFF);
Marshal.WriteByte(page, 0xC, 0xE3);
// protect memory to allow execute code
bool result = VirtualProtect(page, new IntPtr(4096), PAGE_EXECUTE_READ, out int oldProtect);
var getTempPathTrampoline = Marshal.GetDelegateForFunctionPointer<GetTempPath>(page);
StringBuilder builder = new StringBuilder(1024);
getTempPathTrampoline(1024, builder);
Console.WriteLine(builder.ToString());
}
public static int ManagedMethod(int nBufferLength, StringBuilder buffer)
{
buffer.Append("Hello World");
return 0;
}
11 Comments
movabs r11, absoluteAddressOf1Function jmp r11
Func<YourResponse, YourParam1, YourParam2>?