1

I'm trying to call a win api function in an external process.

Basic rundown:

  • Get base address of user32.dll from putty.exe (or any 32 bit proc)
  • Get base address of MessageBoxA from user32.dll
  • Populate MessageBoxA struct with data, and allocate data locally.
  • Allocate struct in putty
  • Write struct to putty.
  • CreateRemoteThread to execute messageboxA.

All WinAPI calls succeed, and no error is thrown, nor does the putty process crash. Simply does not show messagebox.

Any help is greatly appreciated.
Thanks!

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace remote_api_call
{
    class Program
    {
        #region API
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
        static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
            uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateRemoteThread(IntPtr hProcess,
            IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
        #endregion
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
        // privileges
        const int PROCESS_CREATE_THREAD = 0x0002;
        const int PROCESS_QUERY_INFORMATION = 0x0400;
        const int PROCESS_VM_OPERATION = 0x0008;
        const int PROCESS_VM_WRITE = 0x0020;
        const int PROCESS_VM_READ = 0x0010;

        // used for memory allocation
        const uint MEM_COMMIT = 0x00001000;
        const uint MEM_RESERVE = 0x00002000;
        const uint PAGE_READWRITE = 4;


        [StructLayout(LayoutKind.Sequential)]
        struct MessageBoxA
        {
            public IntPtr HWND;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpText;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpCaption;
            [MarshalAs(UnmanagedType.U4)]
            public uint type;
        }

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
        static void Main(string[] args)
        {
            Process putty = Process.GetProcessesByName("putty")[0];

            IntPtr user32Calc = ModuleAddr(calc, "user32.dll");
            Console.WriteLine("User32.dll addr from putty.exe: " + user32Calc.ToInt32());

            IntPtr MessageBoxA = GetProcAddress(user32Calc, "MessageBoxA");
            Console.WriteLine("MessageBoxA addr from putty.exe: " + MessageBoxA.ToInt32());

            IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, putty.Id);


            MessageBoxA rtp = new MessageBoxA();
            rtp.HWND = IntPtr.Zero;
            rtp.lpText = "Hey mate!";
            rtp.lpCaption = "Caption";
            rtp.type = 16;
            UIntPtr bytesWritten;

            // allocate mem locally
            IntPtr iptrtoparams = Marshal.AllocHGlobal(Marshal.SizeOf(rtp));

            // copy data to structure
            Marshal.StructureToPtr(rtp, iptrtoparams, false);

            // allocate mem in other process for params
            IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)Marshal.SizeOf(rtp), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
            if (allocMemAddress != IntPtr.Zero)
                Console.WriteLine("allocmem success");
            if (WriteProcessMemory(procHandle, allocMemAddress, iptrtoparams, (uint)Marshal.SizeOf(rtp), out bytesWritten))
                Console.WriteLine("wpm success");
            //   Marshal.FreeHGlobal(iptrtoparams);
            if (CreateRemoteThread(procHandle, IntPtr.Zero, 0, MessageBoxA, allocMemAddress, 0, IntPtr.Zero) != IntPtr.Zero)
                Console.WriteLine("CreateRemoteThread success");


            Console.Read();
        }

        private static IntPtr ModuleAddr(Process p, string moduleName)
        {
            foreach (ProcessModule pMod in p.Modules)
            {
                if (pMod.ModuleName.ToLower() == moduleName.ToLower())
                    return pMod.BaseAddress;
            }
            return IntPtr.Zero;
        }

    }
}
SeraZheng
  • 187
  • 8
debug
  • 11
  • 3
  • 1
    `struct MessageBoxA` makes no sense. – Raymond Chen Jan 21 '16 at 04:33
  • You don't perform error checking and so you have no idea whether or not function calls succeed. Please correct that before asking. Beyond that `MessageBoxA` has the wrong signature for a thread proc. Your approach is doomed to fail. It will never work. – David Heffernan Jan 21 '16 at 07:07
  • @DavidHeffernan what is the correct signature for the struct? – debug Jan 21 '16 at 15:17
  • A thread proc is not a struct. It is a procedure. Its signature is clearly described in the documentation for `CreateRemoteThread`. Did you read that carefully? Why are you not checking for errors? – David Heffernan Jan 21 '16 at 15:20
  • I checked for errors after every win api call using Marshal.GetLastWin32Error(), and found none. @DavidHeffernan – debug Jan 21 '16 at 15:43
  • Well, no you didn't. The code says otherwise. Furthermore, that's not how to check for errors. You have to check return values. Only if they indicate failure, and docs say to call `GetLastError`, do you call `GetLastError`. – David Heffernan Jan 21 '16 at 16:19
  • I've since added into my code. And I'm obviously checking the return value and not just calling getlastwin32 error. If you're going to continue being an crass ass, don't bother replying. – debug Jan 21 '16 at 17:54
  • The rest of his point still holds. If you are going to inject code into another process, you have to inject valid code. Shoving parameters somewhere random (in the wrong order, mind) and then calling `MessageBoxA()` with a pointer to those parameters is not correct. If you really want to go with the approach you are taking now, you will need to generate real code with a real [`ThreadProc`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms686736%28v=vs.85%29.aspx). In particular, be sure to study the stdcall calling convention and how parameters are passed to functions. – andlabs Jan 21 '16 at 18:47

1 Answers1

0

It is not possible to call an external function using CreateRemoteThread() with multiple arguments. It is possible with 1 argument, but not more than 1.

All the tutorials that say you can do this by passing a structure of arguments are not correct.

Nothing that CreateRemoteThread() does will lead to the arguments being pushed onto the stack and into the proper registers for the function to access. Only 1 argument will be passed correctly, the things you pass in struct will not be accessed.

You can easily see this by putting a read breakpoint on the second argument in your struct, it will never break.

Learn more in my other answer Passing multiple parameters using CreateRemoteThread in C#

GuidedHacking
  • 3,628
  • 1
  • 9
  • 59