lovingxiaobing 发表于 2019-2-9 13:16

[WIN32]简单的办法获取父进程信息(PID|进程名)



硬核开场~~~~


下面是例子用到的相关的函数:
。NtQueryInformationProcess
。QueryFullProcessImageName
。GetProcessImageFileName
。OpenProcess
。GetCurrentProcessId
。PathStripPath


这些函数以及参数的解释在MSDN上都可以查到,虽然有一个“NtQueryInformationProcess”是“未公开函数”,但是在头文件"winternl.h"中提供了它的声明。


NtQueryInformationProcess 函数用到了一个重要的结构:

typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PPEB PebBaseAddress;
    PVOID Reserved2;
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION;



经过前辈的探索,得知结构体中的"Reserved3"字段就是父进程的PID
得到父进程PID后我们再打开这个进程就可以获取到这个进程的相关信息了。
在使用QueryFullProcessImageName、NtQueryInformationProcess或者GetProcessImageFileName时,要求用PROCESS_QUERY_INFORMATION的方式打开


因为这只是获取信息,我们不希望去打扰人家。。。
但是“QueryFullProcessImageName”和“GetProcessImageFileName”这两个函数的要求时必须用PROCESS_QUERY_INFORMATION 或 PROCESS_QUERY_LIMITED_INFORMATION的方式打开的进程句柄。



#ifndef UNICODE
#define UNICODE
#endif // UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif // _UNICODE

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <winternl.h>
#include <Shlwapi.h>

/*
/// GetProcessImageFileName 函数
//windows7: kernel32.dll|.lib
// windows r8+: psapi.dll|.lib
#include <psapi.h>
*/

#pragma comment(lib, "shlwapi.lib")

#define SETVALUEFROMPOINTER(p, v) (*p=v)
#if defined(UNICODE) || defined(_UNICODE)
#define OutPutStr(f, v) wprintf_s(L##f, v)
#else
#define OutPutStr(f, v) printf_s(f, v)
#endif


DWORD GetParentPIDAndName( DWORD ProcessID, LPTSTR lpszBuffer_Parent_Name, PDWORD ErrCodeForBuffer );

int main(int argc, const char* argv[]) {
    DWORD pid;
    TCHAR buf = {0};
    DWORD err_code;

    pid = GetParentPIDAndName(GetCurrentProcessId(), buf, &err_code);

    if ( err_code ) {
      fprintf(stderr, "GetProcessName--> err code: %lu\n", err_code);
    }

    OutPutStr("ParentProcessPID: %lu\n", pid);
    OutPutStr("ParentProcessFullName: %s\n", buf);
    PathStripPath(buf);
    OutPutStr("ParentProcessName: %s\n", buf);

    return 0;
}

typedef
__kernel_entry NTSTATUS
(NTAPI*NQIP)(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

DWORD GetParentPIDAndName( DWORD ProcessID, LPTSTR lpszBuffer_Parent_Name, PDWORD ErrCodeForBuffer ) {
    /// 打开给定进程PID
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ProcessID);
    if ( !ProcessID ) {
      DWORD err_code = GetLastError();
      fprintf_s(stderr, "err code: %lu\n", err_code);
      return 0;
    }

    /// 下面是获取函数 NtQueryInformationProcess 的函数指针
    HMODULE hNtdll = GetModuleHandle(_T("ntdll.dll"));
    if ( !hNtdll ) {
      DWORD err_code = GetLastError();
      fprintf_s(stderr, "err code: %lu\n", err_code);
      CloseHandle(hProcess);
      return 0;
    }

    NQIP _NtQueryInformationProcess = (NQIP)GetProcAddress(hNtdll, "NtQueryInformationProcess");
    if ( !_NtQueryInformationProcess ) {
      DWORD err_code = GetLastError();
      fprintf_s(stderr, "err code: %lu\n", err_code);
      CloseHandle(hProcess);
      return 0;
    }
    //***

    /// 获取打开的进程的进程进程信息
    PROCESS_BASIC_INFORMATION pbi;
    NTSTATUS status = _NtQueryInformationProcess(
                            hProcess,
                            ProcessBasicInformation,
                            (LPVOID)&pbi, sizeof(PROCESS_BASIC_INFORMATION),
                            NULL);

    DWORD dwParentID = 0;
    if ( NT_SUCCESS(status) ) {
      /// 结构体 PROCESS_BASIC_INFORMATION 的 "Reserved3"字段 是父进程的PID
      dwParentID = (LONG_PTR)pbi.Reserved3;

      if ( NULL != lpszBuffer_Parent_Name ) {
            HANDLE hParentProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwParentID);
            if ( hParentProcess ) {
                /// 用来接收进程文件名和路径的长度(必须!)
                DWORD bufs;
                /// 获取进程路径
                BOOL ret = QueryFullProcessImageName(hParentProcess, 0, lpszBuffer_Parent_Name, &bufs);
                if ( TRUE == ret )
                                        SETVALUEFROMPOINTER(ErrCodeForBuffer, 0);
                else
                  SETVALUEFROMPOINTER(ErrCodeForBuffer, GetLastError());

                /*
                /// 结果是DOS路径+文件名
                TCHAR buf;
                GetProcessImageFileName(hParentProcess, buf, BUFSIZ);
                cout << ": " << buf << endl;
                */
            }
            else {
                SETVALUEFROMPOINTER(ErrCodeForBuffer, GetLastError());
            }
            if ( hParentProcess )
                CloseHandle(hParentProcess);
      }
    }
    else {
      DWORD err_code = GetLastError();
      fprintf_s(stderr, "err code: %lu\n", err_code);
    }

    CloseHandle(hProcess);
    return dwParentID;
}



“QueryFullProcessImageName”和“GetProcessImageFileName”的区别是,前者反回的是我们平常常见且使用的win32路径,后者返回的是完整的设备路径。
“GetProcessImageFileName”的介绍点击这儿

我编译运行的工具是codebocks+vs2010附带的编译工具(msvc)(cl-16.00.30319.01和link-10.00.30319.01)
codeblocks 运行一个程序是使用“cb_console_runner.exe”来启动的,所以它就是父进程。


结果:


完结撒花❀❀❀❀❀❀❀❀❀

希望这篇文章能对有需要的朋友提供帮助。。。

450046181 发表于 2019-2-9 13:47

大佬,请问这里怎么补救?

lovingxiaobing 发表于 2019-2-9 13:58

本帖最后由 lovingxiaobing 于 2019-2-9 14:00 编辑

450046181 发表于 2019-2-9 13:47
大佬,请问这里怎么补救?
WTF? 您的VS没有找到这个函数。。。。
函数的MSDN文档:
https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-queryfullprocessimagenamea

如果是提示“连接不到外部符号”等类似的提示的话自己
#pragma comment(lib, "kernel32.lib")
如果还是找不到就自己用GetProcAddress导出来吧:$qqq
这个函数再 “Kernel32.dll” 里

450046181 发表于 2019-2-9 14:30

谢谢你,教的很细心,呵呵~!{:1_893:}

加油 发表于 2019-2-9 15:41

寻觅背影 发表于 2019-2-10 13:34

谢谢分享
页: [1]
查看完整版本: [WIN32]简单的办法获取父进程信息(PID|进程名)