首先编辑一个测试程序,他会将自己特定变量与函数的地址打印出来,并且实时显示变量值
#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,如果想要创建一个程序,读取和调用它们,如何做
下面这种方式
运行后发现是错误的数据。原因就是现代操作系统中,每个进程都有自己独立的虚拟地址。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值
4.用OpenProcess打开查到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。
可以在目标进程中看到被修改的变量,以及函数被调用的提示
