【问题标题】:C# Call win api in external processC#在外部进程中调用win api
【发布时间】:2016-01-21 03:11:51
【问题描述】:

我正在尝试在外部进程中调用 win api 函数。

基本纲要:

  • 从 putty.exe(或任何 32 位 proc)获取 user32.dll 的基地址
  • 从user32.dll中获取MessageBoxA的基地址
  • 用数据填充 MessageBoxA 结构,并在本地分配数据。
  • 在腻子中分配结构
  • 将结构写入腻子。
  • CreateRemoteThread 以执行 messageboxA。

所有 WinAPI 调用都成功,不会抛出错误,putty 进程也不会崩溃。根本不显示消息框。

非常感谢任何帮助。
谢谢!

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;
        }

    }
}

【问题讨论】:

  • struct MessageBoxA 没有意义。
  • 您不执行错误检查,因此您不知道函数调用是否成功。请在提问前更正。除此之外,MessageBoxA 的线程进程签名错误。你的方法注定要失败。它永远不会起作用。
  • @DavidHeffernan 结构的正确签名是什么?
  • 线程过程不是结构。这是一个程序。 CreateRemoteThread 的文档中清楚地描述了它的签名。你仔细阅读了吗?为什么不检查错误?
  • 我在每次使用 Marshal.GetLastWin32Error() 调用 win api 后检查了错误,但没有发现任何错误。 @大卫赫弗南

标签: c# winapi


【解决方案1】:

不能使用带有多个参数的 CreateRemoteThread() 调用外部函数。可以有 1 个参数,但不能超过 1 个。

所有说你可以通过传递参数结构来做到这一点的教程都是不正确的。

CreateRemoteThread() 所做的任何事情都不会导致参数被压入堆栈并压入适当的寄存器以供函数访问。只有1个参数会被正确传递,你传入struct的东西不会被访问。

您可以通过在结构的第二个参数上放置一个读取断点来轻松看到这一点,它永远不会中断。

在我的另一个答案Passing multiple parameters using CreateRemoteThread in C#中了解更多信息

【讨论】:

    猜你喜欢
    • 2015-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    相关资源
    最近更新 更多