You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Active Directory凭据远程运行程序的.NET/C++实现咨询

Absolutely! Both .NET and C++ can absolutely handle this scenario—using valid Active Directory credentials to copy an EXE to a remote domain-joined PC and run it is totally feasible. Let’s break down how to approach this for each platform, with practical code examples and key considerations.

.NET Implementation

.NET provides several built-in and P/Invoke-based tools to handle impersonation, file transfer, and remote process execution. Here’s a step-by-step approach:

1. Impersonate the AD User

First, you need to impersonate the AD account to gain the necessary permissions on the remote machine. Using the LogonUser Win32 API via P/Invoke is a reliable method:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

public class ImpersonationHelper
{
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern bool LogonUser(
        string lpszUsername,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        out IntPtr phToken);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public extern static bool CloseHandle(IntPtr handle);

    public static WindowsImpersonationContext ImpersonateUser(string username, string domain, string password)
    {
        IntPtr userToken = IntPtr.Zero;
        // LOGON32_LOGON_NEW_CREDENTIALS = 9, suitable for remote resource access
        bool success = LogonUser(username, domain, password, 9, 0, out userToken); 

        if (!success)
            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

        WindowsIdentity identity = new WindowsIdentity(userToken);
        CloseHandle(userToken);
        return identity.Impersonate();
    }
}

2. Copy the EXE to the Remote Machine

With impersonation active, you can copy the EXE to a shared folder on the remote machine (like the default Admin$ share, or a custom temp folder):

string sourceExePath = @"C:\local\myapp.exe";
string remoteMachineName = "REMOTE-PC-NAME";
string remoteExePath = @$"\\{remoteMachineName}\C$\temp\myapp.exe";

// Create remote temp folder if it doesn't exist
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(remoteExePath));

// Copy the executable file
System.IO.File.Copy(sourceExePath, remoteExePath, overwrite: true);

3. Execute the Remote Process

Use WMI (Windows Management Instrumentation) to start the process on the remote machine—it’s well-supported in .NET and integrates seamlessly with domain credentials:

using System.Management;

public static void StartRemoteProcess(string remoteMachine, string domain, string username, string password, string exePath)
{
    ConnectionOptions options = new ConnectionOptions
    {
        Username = $@"{domain}\{username}",
        Password = password,
        Impersonation = ImpersonationLevel.Impersonate,
        Authentication = AuthenticationLevel.Default
    };

    ManagementScope scope = new ManagementScope($@"\\{remoteMachine}\root\cimv2", options);
    scope.Connect();

    ManagementClass processClass = new ManagementClass(scope, new ManagementPath("Win32_Process"), new ObjectGetOptions());

    ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
    inParams["CommandLine"] = exePath;

    ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);
    uint returnValue = (uint)outParams["returnValue"];
    if (returnValue != 0)
        throw new Exception($"Failed to start remote process. Error code: {returnValue}");
}

Key Notes for .NET:

  • Ensure the AD user has administrative privileges on the remote machine, or at least permissions to access the shared folder and start processes.
  • The remote machine must have WMI enabled (default on domain-joined PCs) and firewall rules allowing WMI traffic (ports 135 and dynamic RPC ports).
  • Never hardcode credentials—use secure storage like Windows Credential Manager to retrieve them at runtime.
C++ Implementation

For C++, you’ll use Win32 APIs directly. The workflow mirrors the .NET approach: impersonate the user, copy the file, then start the remote process.

1. Impersonate the AD User

Use LogonUser and ImpersonateLoggedOnUser APIs to assume the AD user’s context:

#include <windows.h>
#include <iostream>

HANDLE ImpersonateADUser(LPCWSTR username, LPCWSTR domain, LPCWSTR password)
{
    HANDLE hToken = NULL;
    BOOL success = LogonUserW(
        username,
        domain,
        password,
        LOGON32_LOGON_NEW_CREDENTIALS,
        LOGON32_PROVIDER_DEFAULT,
        &hToken);

    if (!success)
    {
        DWORD err = GetLastError();
        std::cerr << "LogonUser failed. Error: " << err << std::endl;
        return NULL;
    }

    if (!ImpersonateLoggedOnUser(hToken))
    {
        DWORD err = GetLastError();
        std::cerr << "ImpersonateLoggedOnUser failed. Error: " << err << std::endl;
        CloseHandle(hToken);
        return NULL;
    }

    return hToken;
}

2. Copy the EXE to the Remote Machine

Use CopyFileW to transfer the executable to the remote share:

#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

BOOL CopyExeToRemote(LPCWSTR localPath, LPCWSTR remotePath)
{
    // Create remote directory if it doesn't exist
    WCHAR remoteDir[MAX_PATH];
    wcscpy_s(remoteDir, remotePath);
    PathRemoveFileSpecW(remoteDir);

    if (!CreateDirectoryW(remoteDir, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
    {
        std::cerr << "Failed to create remote directory. Error: " << GetLastError() << std::endl;
        return FALSE;
    }

    return CopyFileW(localPath, remotePath, FALSE);
}

3. Execute the Remote Process

Use WMI via COM to start the process on the remote machine:

#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")

BOOL StartRemoteProcessCplusplus(LPCWSTR remoteMachine, LPCWSTR domain, LPCWSTR username, LPCWSTR password, LPCWSTR exePath)
{
    HRESULT hres;

    // Initialize COM
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres))
    {
        std::cerr << "CoInitializeEx failed. HRESULT: 0x" << std::hex << hres << std::endl;
        return FALSE;
    }

    // Set COM security levels
    hres = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE,
        NULL);

    if (FAILED(hres))
    {
        std::cerr << "CoInitializeSecurity failed. HRESULT: 0x" << std::hex << hres << std::endl;
        CoUninitialize();
        return FALSE;
    }

    // Obtain WMI locator
    IWbemLocator* pLoc = NULL;
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
    if (FAILED(hres))
    {
        std::cerr << "CoCreateInstance failed. HRESULT: 0x" << std::hex << hres << std::endl;
        CoUninitialize();
        return FALSE;
    }

    // Connect to remote WMI service
    IWbemServices* pSvc = NULL;
    BSTR serverPath = SysAllocStringLen(NULL, wcslen(remoteMachine) + 15);
    swprintf_s(serverPath, L"\\\\%s\\ROOT\\CIMV2", remoteMachine);

    COAUTHIDENTITY authIdent = { 0 };
    authIdent.User = (USHORT*)username;
    authIdent.UserLength = (USHORT)wcslen(username);
    authIdent.Domain = (USHORT*)domain;
    authIdent.DomainLength = (USHORT)wcslen(domain);
    authIdent.Password = (USHORT*)password;
    authIdent.PasswordLength = (USHORT)wcslen(password);
    authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

    COAUTHINFO authInfo = { 0 };
    authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT;
    authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE;
    authInfo.pwszServerPrincName = NULL;
    authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT;
    authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
    authInfo.pAuthIdentityData = &authIdent;
    authInfo.dwCapabilities = EOAC_NONE;

    CONNECTINFO connectInfo = { 0 };
    connectInfo.pAuthInfo = &authInfo;

    hres = pLoc->ConnectServer(
        serverPath,
        NULL,
        NULL,
        0,
        NULL,
        0,
        0,
        &pSvc);

    SysFreeString(serverPath);
    if (FAILED(hres))
    {
        std::cerr << "ConnectServer failed. HRESULT: 0x" << std::hex << hres << std::endl;
        pLoc->Release();
        CoUninitialize();
        return FALSE;
    }

    // Set proxy security blanket
    hres = CoSetProxyBlanket(
        pSvc,
        RPC_C_AUTHN_WINNT,
        RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        &authIdent,
        EOAC_NONE);

    if (FAILED(hres))
    {
        std::cerr << "CoSetProxyBlanket failed. HRESULT: 0x" << std::hex << hres << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return FALSE;
    }

    // Execute Win32_Process.Create method
    IWbemClassObject* pProcessClass = NULL;
    hres = pSvc->GetObject(L"Win32_Process", 0, NULL, &pProcessClass, NULL);
    if (FAILED(hres))
    {
        std::cerr << "GetObject failed. HRESULT: 0x" << std::hex << hres << std::endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return FALSE;
    }

    IWbemClassObject* pInParams = NULL;
    hres = pProcessClass->GetMethod(L"Create", 0, &pInParams, NULL);
    if (FAILED(hres))
    {
        std::cerr << "GetMethod failed. HRESULT: 0x" << std::hex << hres << std::endl;
        pProcessClass->Release();
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return FALSE;
    }

    VARIANT cmdLine;
    VariantInit(&cmdLine);
    cmdLine.vt = VT_BSTR;
    cmdLine.bstrVal = SysAllocString(exePath);
    hres = pInParams->Put(L"CommandLine", 0, &cmdLine, 0);
    VariantClear(&cmdLine);

    if (FAILED(hres))
    {
        std::cerr << "Put failed. HRESULT: 0x" << std::hex << hres << std::endl;
        pInParams->Release();
        pProcessClass->Release();
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return FALSE;
    }

    IWbemClassObject* pOutParams = NULL;
    hres = pSvc->ExecMethod(L"Win32_Process", L"Create", 0, NULL, pInParams, &pOutParams, NULL);
    if (FAILED(hres))
    {
        std::cerr << "ExecMethod failed. HRESULT: 0x" << std::hex << hres << std::endl;
        pInParams->Release();
        pProcessClass->Release();
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return FALSE;
    }

    // Check process start result
    VARIANT returnVal;
    VariantInit(&returnVal);
    hres = pOutParams->Get(L"returnValue", 0, &returnVal, NULL, NULL);
    if (returnVal.lVal != 0)
    {
        std::cerr << "Remote process failed to start. Error code: " << returnVal.lVal << std::endl;
        VariantClear(&returnVal);
        pOutParams->Release();
        pInParams->Release();
        pProcessClass->Release();
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return FALSE;
    }

    // Cleanup COM resources
    VariantClear(&returnVal);
    pOutParams->Release();
    pInParams->Release();
    pProcessClass->Release();
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();

    return TRUE;
}

Key Notes for C++:

  • Link against required libraries: advapi32.lib, kernel32.lib, wbemuuid.lib, shlwapi.lib.
  • Same remote machine requirements apply: WMI enabled, firewall rules allowing WMI traffic, and AD user privileges.
  • Always clean up COM objects and handles to avoid memory leaks.

General Security & Best Practices

  • Never hardcode credentials: Use secure storage mechanisms like Windows Credential Manager to retrieve credentials at runtime.
  • Minimize privileges: Use the least-privileged AD account that has only the necessary permissions (file access, process execution) on the remote machines.
  • Robust error handling: Add detailed error handling for all API calls—remote operations can fail due to network issues, permission problems, or machine availability.
  • Audit actions: Log all remote operations for compliance and troubleshooting purposes.

内容的提问来源于stack exchange,提问作者Process Monitor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:52:41