跨进程使用句柄

  1. 每个程序都有自己 句柄索引表
  2. 这个 句柄索引表 一开始是没有记录的,程序每查询一个 句柄,这个表中就增加一条记录
  3. 相同的 进程在不同程序中 有 不同的 进程句柄/进程id
  4. 在某个程序中 获取到的 进程句柄/进程id 是当前程序的句柄表的索引

    1. 他们保存在一棵树中
    2. 具体的对应关系 由操作系统负责
    3. 进程句柄/进程id 都是索引(指向具体的某个进程)
  5. 所有句柄(所有资源句柄)都遵守这个规则
  6. 句柄的传递的本质是 不同程序之间 共有某个 进程 的句柄记录(只是他们的表中 都有某个 进程实体的 句柄/句柄id(但是他们是不同的,参见第三条))

方案一: 句柄的继承/自动获取

  1. 只有当进程具有父子关系时,才能使用对象句柄的继承性。
  2. CreateProcess创建进程
  3. 父进程创建某个新进程时要设定SECURITY_ATTRIBUTES.bInheritHandle=true

    表示这个进程句柄 可被继承
  4. 父进程创建子进程时要设定bInheritHandles=true

    表示这个子进程 继承 可被继承的句柄

实例


//假定我们启动这个程序作为被传递句柄的程序
char szCmdline[256] = { 0 };
strcpy_s(szCmdline, 256, "H:\\CR31\\windows\\04communicate\\SubProcess\\Debug\\SubProcess.exe");

//获取程序启动的一些 回掉信息
STARTUPINFOA si = { 0 };
si.cb = sizeof(STARTUPINFOA);
PROCESS_INFORMATION pi;

//设置该进程可被 子进程 继承句柄
SECURITY_ATTRIBUTES sa = {0};
sa.bInheritHandle = TRUE;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);

//创建该进程
BOOL bRet = CreateProcessA(NULL,
    szCmdline,
    &sa,
    NULL,
    FALSE,
    CREATE_NEW_CONSOLE,
    NULL,
    NULL,
    &si,
    &pi);

/*
我们在创建一个子进程
只要把 CreateProcessA 中 bInheritHandles 设置为 true就可以继承 这个句柄了
*/

CreateProcessA(
LPCSTR                lpApplicationName,
LPSTR                 lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL                  bInheritHandles,  //这里要设置为true
DWORD                 dwCreationFlags,
LPVOID                lpEnvironment,
LPCSTR                lpCurrentDirectory,
LPSTARTUPINFOA        lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);

方案二: 句柄的拷贝/主动复制

  1. 获取句柄
  2. 拷贝句柄
  3. 本质是把某个句柄记录 附加到 指定子进程的进程索引表中
  4. 只是把句柄记录复制过去了,接收端仍然不知道这个句柄是什么,需要我们通过其他方式传递

获取进程句柄

/*
这两个函数获取到的 是 当前 进程/线程 的进程索引表中的 当前进程/线程的 的句柄id 
不管在那个 进程中调用 结果都是 -1
*/
GetCurrentProcess ()
GetCurrentThread ()

拷贝句柄

DuplicateHandle 

实例

下面例子演示在 ApiDuplicateHandleSource.exe 中创建一个线程,将该线程句柄复制到 ApiDuplicateHandle.exe 中去,在进程 ApiDuplicateHandle.exe 中结束该线程

  1. 先打开ApiDuplicateHandle
  2. 这时候打开 ApiDuplicateHandleSource ,ApiDuplicateHandleSource 会把他的一个线程的句柄记录增加到 ApiDuplicateHandle
  3. 由于 只是增加了 记录 ApiDuplicateHandle 并不会主动感知,所以我们要手动把 生成的句柄发送给 ApiDuplicateHandle
  4. ApiDuplicateHandle 收到 他新增加的句柄后 直接去 kill掉这个句柄

ApiDuplicateHandleSource的CPP

// ApiDuplicateHandleSource.cpp
#include <iostream>
#include <Windows.h>
#include <process.h>
#include <tchar.h>
#include <TlHelp32.h>

using namespace std;

unsigned __stdcall thread (void * lpPragma);
HANDLE GetProcessHandle(LPCTSTR szName);

int main (void)
{
    HANDLE hThread = NULL;
    hThread = (HANDLE)_beginthreadex(NULL, 0, thread, NULL, 0, NULL);
    cout << "My thread handle is " << hThread << endl;

    HANDLE hTarget = NULL;
    BOOL bSucc = FALSE;
    //你是不是想说这里的hThread与调用DuplicateHandle相关?
    bSucc = DuplicateHandle (GetCurrentProcess(),                            //当前进程的源进程句柄
                            hThread,                                         //当前进程中存在的线程资源句柄(内核对象)
                            GetProcessHandle(_T("ApiDuplicateHandle.exe")),  //目标进程的句柄
                            &hTarget,                                        //该 句柄记录 增加到 ApiDuplicateHandle后,在 ApiDuplicateHandle 中的句柄
                            0,                                               //访问的方式
                            FALSE,                                           //得到的句柄能不能被得到的其的进程的子进程继承
                            DUPLICATE_SAME_ACCESS );                         //访问选项
    if (bSucc)
    {
        cout << "句柄复制成功, 其句柄值为:" << hTarget << endl;
    }

    cin.get();
    return 0;
}

unsigned __stdcall thread (void * lpPragma)
{
    while (1)
    {
        Sleep (1000);
        cout << "Please terminal me!" << endl;
    }

    return 0;
}
//根据程序名称 获取进程句柄
HANDLE GetProcessHandle(LPCTSTR szName)
{
    HANDLE hSanpshot = NULL;
    hSanpshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if ( INVALID_HANDLE_VALUE == hSanpshot )
    {
        return NULL;
    }

    PROCESSENTRY32 pe;
    BOOL bOk = FALSE;
    pe.dwSize = sizeof(pe);

    bOk = Process32First (hSanpshot, &pe);
    if (!bOk)
        return NULL;

    do 
    {
        if ( !_tcscmp (pe.szExeFile, szName) )
        {
            return OpenProcess (PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
        }
        bOk = Process32Next (hSanpshot, &pe);
    }
    while (bOk);

    return NULL;
}

ApiDuplicateHandle 的CPP

// ApiDuplicateHandle.cpp
#include <iostream>
#include <Windows.h>
#include <stdlib.h>
#include <process.h>

using namespace std;

int main (void)
{
    HANDLE hRecv = NULL;

    cout << "请输入复制过来的句柄 : " << endl;
    cin >> hRecv;

    TerminateThread(hRecv, 0);

    cin.get();
    return 0;
}

Last modification:October 26, 2018
如果觉得我的文章对你有用,请随意赞赏