问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Windows系统编程:跨进程使用句柄和操作内存详解

创作时间:
作者:
@小白创作中心

Windows系统编程:跨进程使用句柄和操作内存详解

引用
CSDN
1.
https://blog.csdn.net/weixin_58038441/article/details/143100414

在Windows系统编程中,跨进程使用句柄和操作内存是两个重要的技术点。本文将详细介绍如何通过继承和复制方式实现跨进程使用句柄,以及如何通过WinHex和WriteProcessMemory修改进程内存。同时,还会讲解内存属性和内存分页的相关知识。

前言

各位读者,大家好!本文将为大家讲解Windows系统编程中跨进程使用句柄和操作内存的相关知识。通过本文的学习,你将能够掌握如何在不同进程间共享句柄,以及如何通过编程方式修改其他进程的内存。

问题

让我们先抛出一个问题:进程A获得的句柄能否直接给进程B使用?

尝试通过以下代码进行实验:

// A程序
void CADlg::OnBnClickedGethandle()
{
    DWORD dwPid = GetDlgItemInt(IDC_PID);
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    CString strFmt;
    strFmt.Format("%08X", hProc);
    SetDlgItemText(IDC_HANDLE, strFmt);
}

// B程序
void CBDlg::OnBnClickedUsehandle()
{
    CString strHandle;
    GetDlgItemText(IDC_HANDLE, strHandle);
    HANDLE hProc = (HANDLE)strtoul(strHandle.GetBuffer(), NULL, 16);
    if (TerminateProcess(hProc, 0))
    {
        AfxMessageBox("结束成功!");
    }
    else
    {
        AfxMessageBox("结束失败!");
    }
}

实验过程:

  • 结论:进程B是无法直接使用A的进程句柄的

OpenProcess

OpenProcess函数用于打开进程对象。

HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);

跨进程使用句柄

方式一:继承 - 子进程继承父进程已经打开的句柄

通过C++类来理解CreateProcessOpenProcess的继承关系。

void CADlg::OnBnClickedGethandle()
{
    DWORD dwPid = GetDlgItemInt(IDC_PID);
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPid);
    CString strFmt;
    strFmt.Format("%08X", hProc);
    SetDlgItemText(IDC_HANDLE, strFmt);
}

void CADlg::OnBnClickedCreatechild()
{
    CString strPath;
    GetDlgItemText(IDC_CHILD, strPath);
    STARTUPINFO si = { };
    si.cb = sizeof(si);
    PROCESS_INFORMATION pi = { };
    BOOL Ret = CreateProcess(
        strPath.GetBuffer(),
        NULL,
        NULL,
        NULL,
        TRUE, // 允许子进程继承父进程的句柄
        0,
        NULL,
        NULL,
        &si,
        &pi
    );
    if (!Ret)
    {
        AfxMessageBox("CreateProcess Failed!");
    }
}

实验过程:

  • 可以观察到通过继承的方式,成功跨进程使用句柄
  • 子进程只能继承在自身被创建之前父进程打开的句柄,自身创建后父进程打开的句柄无法继承

方式二:复制句柄

使用DuplicateHandle函数将句柄表里的句柄拷贝到对方的句柄表里。

BOOL DuplicateHandle(
  [in]  HANDLE   hSourceProcessHandle,
  [in]  HANDLE   hSourceHandle,
  [in]  HANDLE   hTargetProcessHandle,
  [out] LPHANDLE lpTargetHandle,
  [in]  DWORD    dwDesiredAccess,
  [in]  BOOL     bInheritHandle,
  [in]  DWORD    dwOptions
);
void CADlg::OnBnClickedGethandle()
{
    DWORD dwPid = GetDlgItemInt(IDC_PID);
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPid);
    HANDLE hProcSelf = GetCurrentProcess();
    
    DWORD dwPidB = GetDlgItemInt(IDC_PID2);
    HANDLE hProcB = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPidB);
    HANDLE hInB = NULL;
    DuplicateHandle(hProcSelf, hProc, hProcB, &hInB, 0, FALSE, DUPLICATE_SAME_ACCESS);
    CString strFmt;
    strFmt.Format("%08X", hInB);
    SetDlgItemText(IDC_HANDLE, strFmt);
}

实验过程:

  • 适用场景:跨权限操作一些东西,比如有system权限的进程拿到句柄给管理员权限的进程用

跨进程操作内存

设计思路:通过修改进程内存地址,来修改游戏模式

通过WinHex修改内存

通过在WinHex中查找flag的位置,手动修改游戏模式

注意:当程序正在使用时,WinHex无法修改

通过WriteProcessMemory修改内存

函数原型:

BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);

示例代码:

int main()
{
    HWND hWnd = FindWindow(NULL, "游戏");
    DWORD dwPid;
    GetWindowThreadProcessId(hWnd, &dwPid);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    LPVOID pAddr = (LPVOID)0x01B2A00B;
    BYTE btCode = 0xFF;
    WriteProcessMemory(hProcess, pAddr, &btCode, sizeof(btCode), NULL);
    return 0;
}
  • 通过FindWindow查找对应标题
  • GetWindowThreadProcessId获取pid
  • 打开要写入的进程
  • 写入内存

内存属性

  1. 内存访问属性:R读,W写,X执行,C写时拷贝。修改内存访问属性:VirtualProtectEx
  2. ProcessHacker查看内存属性:exe双击 - Memory - Protection。
  3. WriteProcessMemory不用判断内存属性,每次写之前修改内存属性,写完后还原内存属性。
  4. 写完数据不还原属性:会被检测(向只读内存写入数据,看是否触发异常,触发则表明正常)
  5. 内存的基本单位4k【一个分页0x1000】,所以当修改0x1225的内存属性实则修改了0x0 ~ 0x2048的内存属性
BOOL VirtualProtectEx(
 HANDLE hProcess,
 LPVOID lpAddress,
 SIZE_T dwSize,
 DWORD flNewProtect,
 PDWORD lpflOldProtect
);

参数说明:

  • 参数3:修改属性的内存大小。修改内存属性会影响到这一段地址空间涉及到的所有内存分页。跨越页面边界的2字节范围会导致两个页面的保护属性都被更改。
  • 参数4:内存属性
  • PAGE_READONLY:只读
  • PAGE_READWRITE:可读可写
  • PAGE_WRITECOPY:写时拷贝
  • PAGE_EXECUTE:可执行
  • PAGE_EXECUTE_READ:可执行可读
  • PAGE_EXECUTE_READWRITE:可执行可读写
  • PAGE_EXECUTE_WRITECOPY:可执行可写时拷贝

内存分页

  1. 内存分页:大小0x1000(4096),4kb。系统管理内存的基本单位。属性改一字节影响一个分页。
  2. 申请内存时,系统至少分配一个分页,一个字节也分配一个分页。
  3. newmalloc是在系统分配的基础上再次分配,在系统分配的分页中再次分配需要的字节。
  4. 操作系统喜欢将统一权限属性的放在一起的原因是方便管理,且不浪费内存,也更容易维护。

总结

本文介绍了继承、复制句柄的方式跨进程使用句柄,使用WinHex、WriteProcessMemory修改内存,以及内存属性和内存分页的相关知识。这些内容对于Windows系统编程和进程间通信具有重要的参考价值。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号