windows 从 32 位进程获取 64 位进程的命令行字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7446887/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 18:06:02  来源:igfitidea点击:

Get command line string of 64-bit process from 32-bit process

cwindowswinapiwindows-process

提问by Kartlee

The code below works for me well to get command line string of 32-bit process from a 32-bit app, 64-bit process from a 64-bit app and 32-bit process from 64-bit app. This will break if I try to use for 64-bit process from 32-bit app. The reason being the structure size difference in PROCESS_BASIC_INFORMATION and address size. So here are my questions -

下面的代码很适合我从 32 位应用程序获取 32 位进程的命令行字符串,从 64 位应用程序获取 64 位进程和从 64 位应用程序获取 32 位进程的命令行字符串。如果我尝试将 32 位应用程序用于 64 位进程,这将中断。原因是 PROCESS_BASIC_INFORMATION 和地址大小的结构大小差异。所以这里是我的问题 -

1) The suggestion given in process hacker ( http://processhacker.sourceforge.net/forums/viewtopic.php?f=15&t=181) to use wow64 function doesn't seem to work and fail with following error -

1)进程黑客(http://processhacker.sourceforge.net/forums/viewtopic.php?f=15&t=181)中给出的使用 wow64 函数的建议似乎不起作用并且失败并出现以下错误 -

NtWow64ReadVirtualMemory64 error: 8000000D while reading ProcessParameters address from A68291A0004028E0

NtWow64ReadVirtualMemory64 错误:从 A68291A0004028E0 读取 ProcessParameters 地址时出现 8000000D

Has anyone tried this and could successfully get information? I posted the same in their forum asking their opinion.

有没有人试过这个并且可以成功获取信息?我在他们的论坛上发布了同样的内容,征求他们的意见。

2) Is there any other approach to query peb information that can work for x86 and x64 reliably?

2)有没有其他方法可以查询可以可靠地用于 x86 和 x64 的 peb 信息?

int get_cmdline_from_pid( DWORD dwPid, char** cmdLine )
{
    DWORD dw, read;
    HANDLE hProcess;
    NtQueryInformationProcess* pNtQip;
    PROCESS_BASIC_INFORMATION pbInfo;
    UNICODE_STRING cmdline;
    WCHAR* wcmdLine;

    *cmdLine = NULL;

    hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid );
    if( !hProcess )
        return FALSE;

    pNtQip = (NtQueryInformationProcess*) GetProcAddress(GetModuleHandle("ntdll.dll"), 
                                                    "NtQueryInformationProcess");
    if(!pNtQip)
        return FALSE;

    pNtQip(hProcess, PROCESSBASICINFOMATION, &pbInfo, sizeof(pbInfo), NULL);

    #ifdef _WIN64
        ReadProcessMemory(hProcess, pbInfo.PebBaseAddress + 0x20, &dw, sizeof(dw), 
                         &read); 
    #else
        ReadProcessMemory(hProcess, pbInfo.PebBaseAddress + 0x10, &dw, sizeof(dw), 
                          &read); 
    #endif

    #ifdef _WIN64
        ReadProcessMemory(hProcess, (PCHAR)dw+112, &cmdline, sizeof(cmdline), &read);
    #else
        ReadProcessMemory(hProcess, (PCHAR)dw+64, &cmdline, sizeof(cmdline), &read);
    #endif

     wcmdLine = (WCHAR *)malloc(sizeof(char)*(cmdline.Length + 2));
     if( !wcmdLine )
         return FALSE;

     ReadProcessMemory(hProcess, (PVOID)cmdline.Buffer, wcmdLine, 
                  cmdline.Length+2, &read);

     *cmdLine = mmwin32_util_widetoansi(wcmdLine);
     free(wcmdLine);

     CloseHandle(hProcess);

     return TRUE;
}

回答by Simon Mourier

A bit late answer maybe, but here is a code that does it. It supports 32 or 64 bit process, and 32 bit process on WOW64 (meaning you can compile for Win32 and X64). It uses undocumented functions, so use at your own risk :-)

答案可能有点晚,但这里有一个代码可以做到。它支持 32 位或 64 位进程,以及 WOW64 上的 32 位进程(意味着您可以为 Win32 和 X64 编译)。它使用未记录的功能,因此使用风险自负:-)

GetCmdLine.cpp:

获取CmdLine.cpp:

#include "stdafx.h"
#include "GetCmdLine.h"

int _tmain(int argc, _TCHAR* argv[])
{
    if (argc < 2)
    {
        printf("Format is GetCmdLine <process id>\n");
        return 0;
    }

    // get process identifier
    DWORD dwId = _wtoi(argv[1]);

    // open the process
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
    DWORD err = 0;
    if (hProcess == NULL)
    {
        printf("OpenProcess %u failed\n", dwId);
        err = GetLastError();
        return -1;
    }

    // determine if 64 or 32-bit processor
    SYSTEM_INFO si;
    GetNativeSystemInfo(&si);

    // determine if this process is running on WOW64
    BOOL wow;
    IsWow64Process(GetCurrentProcess(), &wow);

    // use WinDbg "dt ntdll!_PEB" command and search for ProcessParameters offset to find the truth out
    DWORD ProcessParametersOffset = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0x20 : 0x10;
    DWORD CommandLineOffset = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0x70 : 0x40;

    // read basic info to get ProcessParameters address, we only need the beginning of PEB
    DWORD pebSize = ProcessParametersOffset + 8;
    PBYTE peb = (PBYTE)malloc(pebSize);
    ZeroMemory(peb, pebSize);

    // read basic info to get CommandLine address, we only need the beginning of ProcessParameters
    DWORD ppSize = CommandLineOffset + 16;
    PBYTE pp = (PBYTE)malloc(ppSize);
    ZeroMemory(pp, ppSize);

    PWSTR cmdLine;

    if (wow)
    {
        // we're running as a 32-bit process in a 64-bit OS
        PROCESS_BASIC_INFORMATION_WOW64 pbi;
        ZeroMemory(&pbi, sizeof(pbi));

        // get process information from 64-bit world
        _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64QueryInformationProcess64");
        err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
        if (err != 0)
        {
            printf("NtWow64QueryInformationProcess64 failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read PEB from 64-bit address space
        _NtWow64ReadVirtualMemory64 read = (_NtWow64ReadVirtualMemory64)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64ReadVirtualMemory64");
        err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
        if (err != 0)
        {
            printf("NtWow64ReadVirtualMemory64 PEB failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read ProcessParameters from 64-bit address space
        PBYTE* parameters = (PBYTE*)*(LPVOID*)(peb + ProcessParametersOffset); // address in remote process adress space
        err = read(hProcess, parameters, pp, ppSize, NULL);
        if (err != 0)
        {
            printf("NtWow64ReadVirtualMemory64 Parameters failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read CommandLine
        UNICODE_STRING_WOW64* pCommandLine = (UNICODE_STRING_WOW64*)(pp + CommandLineOffset);
        cmdLine = (PWSTR)malloc(pCommandLine->MaximumLength);
        err = read(hProcess, pCommandLine->Buffer, cmdLine, pCommandLine->MaximumLength, NULL);
        if (err != 0)
        {
            printf("NtWow64ReadVirtualMemory64 Parameters failed\n");
            CloseHandle(hProcess);
            return -1;
        }
    }
    else
    {
        // we're running as a 32-bit process in a 32-bit OS, or as a 64-bit process in a 64-bit OS
        PROCESS_BASIC_INFORMATION pbi;
        ZeroMemory(&pbi, sizeof(pbi));

        // get process information
        _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
        err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
        if (err != 0)
        {
            printf("NtQueryInformationProcess failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read PEB
        if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL))
        {
            printf("ReadProcessMemory PEB failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read ProcessParameters
        PBYTE* parameters = (PBYTE*)*(LPVOID*)(peb + ProcessParametersOffset); // address in remote process adress space
        if (!ReadProcessMemory(hProcess, parameters, pp, ppSize, NULL))
        {
            printf("ReadProcessMemory Parameters failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read CommandLine
        UNICODE_STRING* pCommandLine = (UNICODE_STRING*)(pp + CommandLineOffset);
        cmdLine = (PWSTR)malloc(pCommandLine->MaximumLength);
        if (!ReadProcessMemory(hProcess, pCommandLine->Buffer, cmdLine, pCommandLine->MaximumLength, NULL))
        {
            printf("ReadProcessMemory Parameters failed\n");
            CloseHandle(hProcess);
            return -1;
        }
    }
    printf("%S\n", cmdLine);
    return 0;
}

GetCmdLine.h:

GetCmdLine.h:

#pragma once
#include "stdafx.h"

// NtQueryInformationProcess for pure 32 and 64-bit processes
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
    IN HANDLE ProcessHandle,
    ULONG ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

typedef NTSTATUS (NTAPI *_NtReadVirtualMemory)(
    IN HANDLE ProcessHandle,
    IN PVOID BaseAddress,
    OUT PVOID Buffer,
    IN SIZE_T Size,
    OUT PSIZE_T NumberOfBytesRead);

// NtQueryInformationProcess for 32-bit process on WOW64
typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(
    IN HANDLE ProcessHandle,
    IN PVOID64 BaseAddress,
    OUT PVOID Buffer,
    IN ULONG64 Size,
    OUT PULONG64 NumberOfBytesRead);

// PROCESS_BASIC_INFORMATION for pure 32 and 64-bit processes
typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PVOID PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;

// PROCESS_BASIC_INFORMATION for 32-bit process on WOW64
// The definition is quite funky, as we just lazily doubled sizes to match offsets...
typedef struct _PROCESS_BASIC_INFORMATION_WOW64 {
    PVOID Reserved1[2];
    PVOID64 PebBaseAddress;
    PVOID Reserved2[4];
    ULONG_PTR UniqueProcessId[2];
    PVOID Reserved3[2];
} PROCESS_BASIC_INFORMATION_WOW64;

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING;

typedef struct _UNICODE_STRING_WOW64 {
  USHORT Length;
  USHORT MaximumLength;
  PVOID64 Buffer;
} UNICODE_STRING_WOW64;

回答by David Heffernan

Your 32 bit pointers are not wide enough to store addresses in the 64 bit address space of the target process and will be truncated. Thus, what you are attempting is impossible. This is one of the situations where Raymond Chen would advise you to stop using the emulator.

您的 32 位指针不够宽,无法在目标进程的 64 位地址空间中存储地址,将被截断。因此,您正在尝试的事情是不可能的。这是 Raymond Chen 建议您停止使用模拟器的情况之一。

Having invoked Raymond Chen's name, I did a quick search to see if he had any useful nuggets. That search turned up this article: Why is there no supported way to get the command line of another process?. The useful nugget is the observation that Win32_Process.CommandLinegives you what you need (somehow). So, my advice is to give WMI a go.

在提到了 Raymond Chen 的名字后,我快速搜索了他是否有任何有用的掘金。该搜索出现了这篇文章:为什么没有支持的方法来获取另一个进程的命令行?. 有用的金块是观察,它Win32_Process.CommandLine为您提供所需的东西(不知何故)。所以,我的建议是试试 WMI。