VCpp程序读取和修改其它进程的变量,调用其它进程的函数

⌚Time: 2023-02-02 01:05:49

👨‍💻Author: Jack Ge

首先编辑一个测试程序,他会将自己特定变量与函数的地址打印出来,并且实时显示变量值


#include <iostream>

#include <thread>

using namespace std;

//函数声明

int mutiply_2(void*);

//函数类型定义

typedef int (*Func)(void*);



int main()

{

    //定义变量

    int number = 1234;

    int *pNumber = &number;

    //定义函数指针

    Func myFunc = NULL;

    myFunc = mutiply_2;

    //打印变量和函数的基址

    printf("variable address:%p func address: %p\n",pNumber,myFunc);

    //打印变量值

    while(true){

        printf("variable number is:%d\n",*pNumber);

        //睡眠1s

        std::this_thread::sleep_for(std::chrono::milliseconds(1000));

    }

    return 0;

}

int mutiply_2(void *pData){

    cout<<"mutiply_2 function is called"<<endl;

    return *(int*)pData * 2;

}


可以看到它的number变量地址为0x0061FE74,mutiply_2函数地址为0X004016A8,如果想要创建一个程序,读取和调用它们,如何做

下面这种方式


int *p = 0x0061FE74;

printf("%d\n",*p);

运行后发现是错误的数据。原因就是现代操作系统中,每个进程都有自己独立的虚拟地址。0x0061FE74是本进程的地址,读取和修改的是本进程的数据,而不能操作目标进程的这个地址,如何修改目标进程的特定地址里的内容?

windows操作系统提供了一些API使得我们可以在一个进程里修改另一个进程的内容,最常用的是共享内存,不过这种方式需要两个进程提前“协商”好,而大多数程序不希望其他程序随意修改它的内存。

除了这种办法,可以利用一些工具比如CE,找到需要修改的内存地址,使用Win32API的OpenProcess函数打开另一个进程,然后再调用WriteProcessMemory/WriteProcessMemory函数写入数据。从而达到我们想要的操作。

一般包括以下几步:

1.找到需要修改的变量基址,我让测试程序自己打印出来了


//目标程序的number变量基地址

LPVOID lpVarBaseAddress=(LPVOID)0x0061fe74;

//目标程序的mutiply_2函数基地址

LPVOID lpFunctBaseAddres=(LPVOID)0x004016a8;

2.通过FindWindow读取窗体的句柄


//获取目标程序窗体句柄

HWND hwnd = FindWindow("ConsoleWindowClass","C:\\Users\\m\\Documents\\codeblock\\test\\bin\\Debug\\test.exe");

3.通过GetWindowThreadProcessId读取查找窗体句柄进程的PID值


//获取目标程序线程pid

DWORD pId;

GetWindowThreadProcessId(hwnd,&pId);

4.用OpenProcess打开查到PID值的进程


HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pId);

5.WriteProcessMemory/WriteProcessMemory读出指定的内存地址数据


//读取目标进程中的变量

int numberGet;

ReadProcessMemory(hProcess,lpVarBaseAddress,(void*)&numberGet,sizeof(DWORD),0 );

cout<<"读取目标程序变量值:"<<numberGet<<endl;

//修改目标程序变量值

numberGet = 2333;

WriteProcessMemory( hProcess, lpVarBaseAddress, (void*)&numberGet, sizeof(numberGet),NULL);

cout<<"修改目标进程变量值为:"<<numberGet<<endl;

调用其它进程的函数?

使用 CreateRemoteThread函数创建在另一个进程的虚拟地址空间中运行的线程,通过它我们可以调用其它进程中的函数


HANDLE CreateRemoteThread(

  [in]  HANDLE                 hProcess,    //目的进程的句柄

  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,  //指向 SECURITY_ATTRIBUTES 结构的指针,该结构指定新线程的安全描述符

  [in]  SIZE_T                 dwStackSize, //堆栈的初始大小(以字节为单位)。如果此参数为 0 (零) ,则新线程将使用可执行文件的默认大小

  [in]  LPTHREAD_START_ROUTINE lpStartAddress,  //指向由线程执行的 LPTHREAD_START_ROUTINE 类型的应用程序定义函数的指针,表示远程进程中线程的起始地址。 函数必须存在于远程进程中

  [in]  LPVOID                 lpParameter, //指向要传递给线程函数的变量的指针

  [in]  DWORD                  dwCreationFlags, //控制线程创建的标志

  [out] LPDWORD                lpThreadId   //指向接收线程标识符的变量的指针

);

目标函数需要有一个指针类型的参数,我们不能直接将本地的参数给他,因为该参数所指向的是本进程的内存,因此需要在目标进程开辟一块内存,并且将这片它自己的内存区域作为参数传递给他

通过VirtualAllocEx函数可以在指定进程的虚拟地址空间中保留、提交或更改内存区域的状态。


LPVOID VirtualAllocEx(

  [in]           HANDLE hProcess,

  [in, optional] LPVOID lpAddress,

  [in]           SIZE_T dwSize,

  [in]           DWORD  flAllocationType,

  [in]           DWORD  flProtect

);

调用其它进程函数代码


    //调用目标进程的函数

    //函数参数

    int varNum = 999;

    //在目标程序中分配内存

    void *pDataRemote = VirtualAllocEx( hProcess, 0, sizeof(varNum), MEM_COMMIT|MEM_RESERVE,  PAGE_READWRITE );

    //写入参数值到目标程序的内存

    WriteProcessMemory( hProcess, pDataRemote, (void*)&varNum, sizeof(varNum),NULL);

    //建立远程线程,执行目标程序的函数

    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpFunctBaseAddres, pDataRemote, 0 , NULL);

    if (hThread)

    {

        //等待线程执行结束

        WaitForSingleObject( hThread, INFINITE );

        //获取线程函数返回值

        DWORD remoteRet;

        GetExitCodeThread( hThread, &remoteRet );

        cout<<"函数返回值:"<<remoteRet<<endl;

        //关闭线程

        CloseHandle( hThread );

        //释放目标程序中分配的内存

        VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE);

    }

效果

首先读取目标进程的变量值为1234,之后将其修改为2333,并执行目标进程的函数,得到返回值1998。

可以在目标进程中看到被修改的变量,以及函数被调用的提示