Windows编程_Lesson008_内存_内存修改器

内存修改器

修改指定进程名字中的指定值所在的地址,进而改变这个值。

单线程版本
#include <Windows.h>
#include <stdio.h>
#include <vector>


// 第一次查找
void FirstFind(HANDLE hProcess, BYTE *pBuffer, DWORD dwPageSize, DWORD dwVal, std::vector<DWORD> &vecAddr)
{
    DWORD dwOneGB = 1024 * 1024 * 1024;
    for (DWORD dwBaseAddr = 0; dwBaseAddr < dwOneGB*2; dwBaseAddr += dwPageSize)
    {
        // 读取一页大小的内存空间
        if (ReadProcessMemory(hProcess, (LPCVOID)dwBaseAddr, pBuffer, dwPageSize, nullptr))
        {
            // 从一页地址中查找相等的值,并记录
            DWORD *pdw = nullptr;
            for (DWORD i = 0; i < dwPageSize - 3; ++i)
            {
                pdw = (DWORD *)&pBuffer[i];
                if (pdw[0] == dwVal)
                {
                    vecAddr.push_back(dwBaseAddr + i);
                }
            }
        }
    }
}

// 下一次查找
void NextFind(HANDLE hProcess, DWORD dwPageSize, DWORD dwVal, std::vector<DWORD> &vecAddr)
{
    DWORD dwCount = 0;
    DWORD dwSize = vecAddr.size();
    DWORD dwReadVal = 0;
    for (DWORD i=0; i<dwSize; ++i)
    {
        if (ReadProcessMemory(hProcess, (LPCVOID)vecAddr[i], &dwReadVal, sizeof(DWORD), nullptr))
        {
            if (dwReadVal == dwVal)
            {
                vecAddr[dwCount++] = vecAddr[i];
            }
        }
    }
    vecAddr.resize(dwCount);
}


int main()
{
    do
    {
        // 查找窗口
        printf("Please input the process name:");
        char strProcessName[MAXBYTE] = { 0 };
        gets_s(strProcessName, MAXBYTE);
        HWND hWnd = FindWindowA(nullptr, strProcessName);
        //HWND hWnd = FindWindowW(nullptr, L"Plants vs. Zombies 1.2.0.1073 RELEASE");
        if (hWnd == nullptr)
        {
            printf("the handle of window do not found!");
            break;
        }

        // 根据窗口句柄查找进程ID
        DWORD dwProcessId = 0;
        GetWindowThreadProcessId(hWnd, &dwProcessId);
        if (dwProcessId == NULL)
        {
            printf("process Id do not found!");
            break;
        }

        // 打开进程
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
        if (hProcess == nullptr)
        {
            printf("open the process failed!");
            break;
        }

        // 在读取内存的时候,最好以页的大小为单位,这样可以提高读取效率,获取页面大小
        SYSTEM_INFO system_info = { 0 };
        GetSystemInfo(&system_info);
        DWORD dwPageSize = system_info.dwPageSize;
        // 判断是否是32位程序运行在64位操作系统下面,以便于确定起始地址,如果是的话,输出值为TRUE,其余情况都为FALSE
        // 在这里无论是32位系统还是64位系统,它们的起始地址都是0x10000,所以起始地址就固定了,但是对于结束地址,这里就
        // 按照32位查找了,因为64位的太大了,所以暂时没有必要判断多少位系统
        //BOOL bWow64 = FALSE;
        //IsWow64Process(hProcess, &bWow64);
        //if (bWow64)
        //{
        //  
        //}

        BYTE *pByte = new BYTE[dwPageSize];
        memset(pByte, 0, dwPageSize * sizeof(BYTE));
        // 定义一个vector
        std::vector<DWORD> vecAddr;

        DWORD dwVal = 0;
        printf("The value to find:");
        scanf_s("%d", &dwVal);

        DWORD dwPrevTime = GetTickCount();
        FirstFind(hProcess, pByte, dwPageSize, dwVal, vecAddr);
        printf("The first find used time:%d ms\r\n", GetTickCount() - dwPrevTime);

        while (vecAddr.size() > 1)
        {
            printf("The value to find:");
            scanf_s("%d", &dwVal);
            NextFind(hProcess, dwPageSize, dwVal, vecAddr);
        }

        printf("The value to modify:");
        scanf_s("%d", &dwVal);
        WriteProcessMemory(hProcess, (LPVOID)vecAddr[0], &dwVal, sizeof(DWORD), nullptr);
    }
    while (false);

    system("pause");
    return 0;
}
多线程版本
#include <Windows.h>
#include <stdio.h>
#include <vector>
#include <process.h>

typedef struct _tagParam
{
    DWORD dwPageSize;
    DWORD dwStartAddr;
    DWORD dwEndAddr;
    DWORD dwFindVal;
    HANDLE hProcess;
    std::vector<DWORD> vecAddr;
}Param;

unsigned int __stdcall ThreadFunc(void *lParam)
{
    Param *pParam = (Param *)lParam;
    BYTE *pByte = new BYTE[pParam->dwPageSize];
    for (DWORD dwStartAddr=pParam->dwStartAddr; dwStartAddr<pParam->dwEndAddr; dwStartAddr+=pParam->dwPageSize)
    {
        if (ReadProcessMemory(pParam->hProcess, (LPCVOID)dwStartAddr, pByte, pParam->dwPageSize, nullptr))
        {
            DWORD *pDword = nullptr;
            for (DWORD i=0; i<pParam->dwPageSize-3; ++i)
            {
                pDword = (DWORD *)&pByte[i];
                if (pDword[0] == pParam->dwFindVal)
                {
                    pParam->vecAddr.push_back(dwStartAddr + i);
                }
            }
        }
    }

    delete[] pByte;
    return 0;
}

// 第一次查找
void FirstFind(HANDLE hProcess, BYTE *pBuffer, DWORD dwPageSize, DWORD dwVal, std::vector<DWORD> &vecAddr)
{
    DWORD dwOneGB = 1024 * 1024 * 1024;
    for (DWORD dwBaseAddr = 0; dwBaseAddr < dwOneGB*2; dwBaseAddr += dwPageSize)
    {
        // 读取一页大小的内存空间
        if (ReadProcessMemory(hProcess, (LPCVOID)dwBaseAddr, pBuffer, dwPageSize, nullptr))
        {
            // 从一页地址中查找相等的值,并记录
            DWORD *pdw = nullptr;
            for (DWORD i = 0; i < dwPageSize - 3; ++i)
            {
                pdw = (DWORD *)&pBuffer[i];
                if (pdw[0] == dwVal)
                {
                    vecAddr.push_back(dwBaseAddr + i);
                }
            }
        }
    }
}

// 下一次查找
void NextFind(HANDLE hProcess, DWORD dwPageSize, DWORD dwVal, std::vector<DWORD> &vecAddr)
{
    DWORD dwCount = 0;
    DWORD dwSize = vecAddr.size();
    DWORD dwReadVal = 0;
    for (DWORD i=0; i<dwSize; ++i)
    {
        if (ReadProcessMemory(hProcess, (LPCVOID)vecAddr[i], &dwReadVal, sizeof(DWORD), nullptr))
        {
            if (dwReadVal == dwVal)
            {
                vecAddr[dwCount++] = vecAddr[i];
            }
        }
    }
    vecAddr.resize(dwCount);
}

int main()
{
    do
    {
        // 查找窗口
        printf("Please input the process name:");
        char strProcessName[MAXBYTE] = { 0 };
        gets_s(strProcessName, MAXBYTE);
        HWND hWnd = FindWindowA(nullptr, strProcessName);
        //HWND hWnd = FindWindowW(nullptr, L"Plants vs. Zombies 1.2.0.1073 RELEASE");
        if (hWnd == nullptr)
        {
            printf("the handle of window do not found!");
            break;
        }

        // 根据窗口句柄查找进程ID
        DWORD dwProcessId = 0;
        GetWindowThreadProcessId(hWnd, &dwProcessId);
        if (dwProcessId == NULL)
        {
            printf("process Id do not found!");
            break;
        }
        // 打开进程
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
        if (hProcess == nullptr)
        {
            printf("open the process failed!");
            break;
        }
        // 在读取内存的时候,最好以页的大小为单位,这样可以提高读取效率,获取页面大小
        SYSTEM_INFO system_info = { 0 };
        GetSystemInfo(&system_info);

        DWORD dwThreadCount = system_info.dwNumberOfProcessors;

        BYTE *pByte = new BYTE[system_info.dwPageSize];
        memset(pByte, 0, system_info.dwPageSize * sizeof(BYTE));
        // 定义一个vector
        std::vector<DWORD> vecAddr;

        DWORD dwVal = 0;
        printf("The value to find:");
        scanf_s("%d", &dwVal);

        DWORD dwPrevTime = GetTickCount();
        //FirstFind(hProcess, pByte, dwPageSize, dwVal, vecAddr);

        DWORD dwStartAddr = 0;
        DWORD dwOneGB = 1024 * 1024 * 1024;
        DWORD dwSizePerThread = 16 * 1024 * 1024;   // 每个线程读取16MB的空间
        HANDLE *hThreads = new HANDLE[system_info.dwNumberOfProcessors];
        Param *pParams = new Param[system_info.dwNumberOfProcessors];

        for (DWORD i = 0; i < system_info.dwNumberOfProcessors; ++i)
        {
            pParams[i].dwStartAddr = dwStartAddr;
            dwStartAddr += dwSizePerThread;
            pParams[i].dwEndAddr = dwStartAddr;
            pParams[i].hProcess = hProcess;
            pParams[i].dwPageSize = system_info.dwPageSize;
            pParams[i].dwFindVal = dwVal;
            pParams[i].vecAddr.clear();
            hThreads[i] = (HANDLE)_beginthreadex(nullptr, 0, ThreadFunc, &pParams[i], 0, nullptr);
        }

        for (; dwStartAddr<dwOneGB*2; )
        {
            DWORD dwRet = WaitForMultipleObjects(system_info.dwNumberOfProcessors, hThreads, FALSE, INFINITE);
            pParams[dwRet].dwStartAddr = dwStartAddr;
            dwStartAddr += dwSizePerThread;
            pParams[dwRet].dwEndAddr = dwStartAddr;
            for (DWORD i=0; i<pParams[dwRet].vecAddr.size(); ++i)
            {
                vecAddr.push_back(pParams[dwRet].vecAddr[i]);
            }
            pParams[dwRet].vecAddr.clear();
            hThreads[dwRet] = (HANDLE)_beginthreadex(nullptr, 0, ThreadFunc, &pParams[dwRet], 0, nullptr);
        }
        WaitForMultipleObjects(system_info.dwNumberOfProcessors, hThreads, TRUE, INFINITE);

        printf("The first find used time:%d ms\r\n", GetTickCount() - dwPrevTime);

        while (vecAddr.size() > 1)
        {
            printf("The value to find:");
            scanf_s("%d", &dwVal);
            NextFind(hProcess, system_info.dwPageSize, dwVal, vecAddr);
        }

        printf("The value to modify:");
        scanf_s("%d", &dwVal);
        WriteProcessMemory(hProcess, (LPVOID)vecAddr[0], &dwVal, sizeof(DWORD), nullptr);

        delete[] pByte;
    }
    while (false);

    system("pause");
    return 0;
}

虚拟内存页面区块

前面的两个例子,无论是单线程也好,多线程也好,我们的扫描方式都是最普通的方式,从0x0-0x80000000共2GB的内存空间,我们并没有做任何的判断,毫无疑问这样的扫描速度不是很快的,并且占用CPU资源也较多。
我们知道Windows进行管理内存是按照页面大小来管理的,每一个CPU都有它自己的页面大小,现在几乎所有的CPU的页面大小,我们如果想进行快速的扫描的话,应该获取Windows中页面区域中的一些属性,可以使用VirtualQueryEx函数类进行查询。其函数原型如下所示:

SIZE_T WINAPI VirtualQueryEx(
  _In_     HANDLE                    hProcess,
  _In_opt_ LPCVOID                   lpAddress,
  _Out_    PMEMORY_BASIC_INFORMATION lpBuffer,
  _In_     SIZE_T                    dwLength
);

hProcess,要查找进程内存信息的句柄;
lpAddress,页面(一个或者多个)区域的基地址;
lpBuffer,包含了一个进程虚拟地址空间中关于多个页面范围的信息;
**dwLength**MEMORY_BASIC_INFORMATION结构体的大小。

MEMORY_BASIC_INFORMATION结构体:

typedef struct _MEMORY_BASIC_INFORMATION {
  PVOID  BaseAddress;
  PVOID  AllocationBase;
  DWORD  AllocationProtect;
  SIZE_T RegionSize;
  DWORD  State;
  DWORD  Protect;
  DWORD  Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

BaseAddress,指向了多个页面区域的首地址(说明Windows是以页面大小为基本单位进行内存管理);
AllocationBase,由VirtualAlloc函数分配的一系列页面的基地址指针;
AllocationProtect,内存区域被初始化分配时的保护选项;
RegionSize,从具有相同属性的所有页面的基地址开始的内存区域大小,以字节为单位;
State,区域中页面的状态,有三种状态,分别是:

  • MEM_COMMIT(0x1000):指示提交页面的物理存储分配,不是在内存中就是在分页磁盘上的文件中;
  • MEM_FREE (0x10000):指示了对于调用过程和分配不可访问的空闲页面,这个信息在AllocationBase, AllocationProtect, Protect,和Type 参数是未定义的;
  • MEM_RESERVE (0x2000):指示保留页面在一系列进程的虚拟地址空间保留没有任何物理存储分配,对于保留页,Protect参数是未定义的。
    Protect,页面区域的访问保护;
    Type,区域中页面的类型,有以下三种:
  • MEM_IMAGE (0x1000000):指示在区域中的页面内存被映射到镜像区域中的视图
  • MEM_MAPPED (0x40000):指示在区域中的页面内存被映射到区域中的视图
  • MEM_PRIVATE (0x20000):指示在区域中的页面内存是私有的,即不与其它进程共享

下面是简单的使用VirtualQueryEx函数得到本进程中的一些信息的源码:

#include <Windows.h>
#include <stdio.h>

int main()
{
    HANDLE hProcess = GetCurrentProcess();
    MEMORY_BASIC_INFORMATION mbi;
    DWORD dwAddr = 0x0;
    while (sizeof(mbi) == VirtualQueryEx(hProcess, reinterpret_cast<LPVOID>(dwAddr), &mbi, sizeof(mbi)))
    {
        printf("BaseAddr:0x%X\tSize:0x%X\tState:%d\r\n", reinterpret_cast<UINT>(mbi.BaseAddress), mbi.RegionSize, mbi.State);
        dwAddr += static_cast<DWORD>(mbi.RegionSize);
    }

    system("pause");
    return 0;
}

打印结果如下所示
这里写图片描述
这里写图片描述

这个例子会对我们上面的进程地址空间的扫描带来一些便利。
任务:重点了解一下Protect属性,会对以后Windows内存有很好的理解。

动态获取系统内存信息

我们上一小节通过VirtualQueryEx函数可以查询虚拟内存中的信息。
实际上,我们所有的内存都是和系统相关的,所有的内存都是存储在系统中的虚拟内存区域,当需要使用的时候再提交到物理内存中,再进行运行。所以我们还需要对系统中的物理内存有一些了解。
我们可以使用GetSystemInfo函数进行动态的获取系统信息,这个函数需要传递一个SYSTEM_INFO的结构体,下面是源代码:
我们在x86情况下进行调试,

#include <Windows.h>
#include <stdio.h>

int main()
{
    SYSTEM_INFO system_info = { 0 };
    GetSystemInfo(&system_info);
    printf("页面大小:%d\r\n", system_info.dwPageSize);
    printf("分配颗粒:%d\r\n", system_info.dwAllocationGranularity);
    printf("用户区开始地址:%X\r\n", (unsigned int)system_info.lpMinimumApplicationAddress);
    printf("用户区最大地址:%X\r\n", (unsigned int)system_info.lpMaximumApplicationAddress);

    system("pause");
    return 0;
}

打印结果如下所示:
这里写图片描述

如果我们换成x64进行调试,代码该做如何修改呢?
通过查阅文档,我们可以使用GetNativeSystemInfo函数进行获取,参数和GetSystemInfo函数一样,都是试用SYSTEM_INFO结构体。

下面是获取64程序的内存信息源码:

#include <Windows.h>
#include <stdio.h>

int main()
{
    SYSTEM_INFO system_info = { 0 };
    GetNativeSystemInfo(&system_info);
    printf("页面大小:%d\r\n", system_info.dwPageSize);
    printf("分配颗粒:%d\r\n", system_info.dwAllocationGranularity);
    printf("用户区开始地址:%llX\r\n", (unsigned long long)system_info.lpMinimumApplicationAddress);
    printf("用户区最大地址:%llX\r\n", (unsigned long long)system_info.lpMaximumApplicationAddress);

    system("pause");
    return 0;
}

打印结果如下:
这里写图片描述
上面的结果和我们在其他资料看到的信息一样。

获取系统内存大小及空闲内存

有的时候我们在进行编程的时候,比如服务器编程,我们需要知道系统中的总物理内存是多少,使用了多少等信息。
我们可以使用GlobalMemoryStatus函数进行查询,函数原型如下:

void WINAPI GlobalMemoryStatus(
  _Out_ LPMEMORYSTATUS lpBuffer
);

它需要传递一个MEMORYSTATUS结构体,如下

typedef struct _MEMORYSTATUS {
  DWORD  dwLength;
  DWORD  dwMemoryLoad;
  SIZE_T dwTotalPhys;
  SIZE_T dwAvailPhys;
  SIZE_T dwTotalPageFile;
  SIZE_T dwAvailPageFile;
  SIZE_T dwTotalVirtual;
  SIZE_T dwAvailVirtual;
} MEMORYSTATUS, *LPMEMORYSTATUS;

dwLength,MEMORYSTATUS结构体的大小,以字节为单位。(我们会发现,在Windows中有很多结构体需要传入结构体大小的这个参数,这主要是进行版本的匹配,不同的版本,这个结构体的大小可能会发生一些变化)
dwMemoryLoad,0-100的整数,指定了大概指定了物理内存使用的百分比
dwTotalPhys,物理内存的总大小,以字节为单位
dwAvailPhys,物理内存中,可用内存的大小,以字节为单位
dwTotalPageFile,当前限制提交内存的大小,以字节为单位
dwAvailPageFile,当前程序能够提交的最大内存
dwTotalVirtual,用户模式下,调用过程的虚拟地址空间的总大小
dwAvailVirtual,用户模式下,调用过程的虚拟地址空间的可用总大小

下面是获取物理内存信息的源码:

#include <Windows.h>
#include <stdio.h>

int main()
{
    MEMORYSTATUS memorystatus = { 0 };
    GlobalMemoryStatus(&memorystatus);
    printf("使用比例:%d\r\n", memorystatus.dwMemoryLoad);
    printf("总内存大小  :%lld\r\n", memorystatus.dwTotalPhys);
    printf("可用内存大小:%lld\r\n", memorystatus.dwAvailPhys);

    system("pause");
    return 0;
}

打印结果如下:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值