內存搜索

最近上的一門安全課,完成一個作業,需要把一個簡單遊戲的血量鎖定,使其“永生”所以學習了一下相關的知識,現在也記錄下來吧。

大致思路如下:

1) 通過搜索血量的值找出所有保存這個值的地址,
2) 改變血量,在上次結果中搜索改變後的血量,此時結果數會少些,如果不爲1,重複2),如果爲1,此時的地址就是儲存該變量的地址。
3) 將要改變的量寫入該地址

實現細節:

FindFirst();
該函數實現遍歷指定的某個進程的整個內存空間
FindNext();
該函數在FindFirst()的基礎之上,對其或者上一次FindNext()函數執行後的結果列表進行搜索
CompareAPage(DWORD dwBaseAddr, DWORD dwValue)
該函數通過基於頁,比較兩個頁是否一樣。(因爲進程的內存採用分頁機制,所以採用頁爲單位比較兩個值)

程序的思路要跟大家說清楚,就是如果你要修改某個進程裏面某個變量值val(比如遊戲中的血量),首先你在進程中搜索所有保存該值的地址,會得到一個地址列表,這就是FindFirst函數做的事情,然後更改該值,再搜索一遍,這次搜索就是在第一次中的結果列表中搜索了,如果得到的地址不唯一,那麼繼續改變值,再搜索,知道結果唯一,該地址就是儲存該值的,下面就直接把自己想要的值寫入就行了。

下面是console的代碼:(ps.網上可能有類似的,我也不標註了,我看了一下,這是王豔平的windows程序設計上的代碼,權當大家一起學習吧。)
附贈界面化程序代碼

#include <windows.h>
#include <stdio.h>
BOOL CompareAPage(DWORD dwBaseAddr,DWORD dwValue);
BOOL FindFirst(DWORD dwValue);//目標進程空間進行第一次查找
BOOL FindNext(DWORD dwValue);//在目標進程地址空間第2、3、4 ...次查找
DWORD g_arList[1024];//地址列表
int g_nListCnt;//有效地址的個數
HANDLE g_hProcess;//目標進程句柄

BOOL CompareAPage(DWORD dwBaseAddr,DWORD dwValue)
{
    BYTE arBytes[4096];
    if (!::ReadProcessMemory(g_hProcess,(LPVOID)dwBaseAddr,arBytes,4096,NULL))
    {
        return false;
    }
    DWORD * pdw;
    for (int i=0;i<(int)4*1024-3;i++)
    {
        pdw =(DWORD*)&arBytes[i];
        if (pdw[0]==dwValue)//等於要查找的值
        {
            if(g_nListCnt>=1024)
            {
                return FALSE;
            }
            else
            {
                g_arList[g_nListCnt++]=dwBaseAddr+i;
            }
        }
    }
    return true;
}
BOOL FindFirst(DWORD dwValue)
{
    const DWORD dwOneGB=1024*1024*1024;
    const DWORD dwOnePage=4*1024;
    if (g_hProcess==NULL)
    {
        return FALSE; 
    }
    //查看操作系統類型,以決定開始地址
    DWORD dwBase;
    OSVERSIONINFO vi={sizeof(vi)
    };
    ::GetVersionEx(&vi);
    if (vi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
    {
        dwBase=4*1024*1024;//windows98系列,4mb
    }
    else
    {
        dwBase=640*1024;//windowsNT系列,64kb
    }
    //在開始地址到2GB的地址空間進行查找
    for (;dwBase<2*dwOneGB;dwBase+=dwOnePage)
    {
        CompareAPage(dwBase,dwValue);
    }
    return TRUE;
}
void ShowList()
{
    for (int i=0;i<g_nListCnt;i++)
    {
        printf("%08lX\n",g_arList[i]);
    }
}
BOOL FindNext(DWORD dwValue)
{
    //保存m_arList數組中有效地址的個數,初始化新的m_nListCnt值
    int nOrgCnt=g_nListCnt;
    g_nListCnt=0;

    //在m_arList數組記錄的地址處查找
    BOOL bRet=FALSE;//假設失敗
    DWORD dwReadValue;
    for (int i=0;i<nOrgCnt;i++)
    {
        if (::ReadProcessMemory(g_hProcess,(LPVOID)g_arList[i],&dwReadValue,sizeof(DWORD),NULL))
        {
            if (dwValue==dwReadValue)
            {
                g_arList[g_nListCnt++]=g_arList[i];
                bRet=TRUE;
            }
        }
    }
    return bRet;
}
BOOL WriteMemory(DWORD dwAddr,DWORD dwValue)
{
    return ::WriteProcessMemory(g_hProcess,(LPVOID)dwAddr,&dwValue,sizeof(DWORD),NULL);
}
int main()
{
    char szFileName[]="1.exe";
    STARTUPINFO si={sizeof(si)};
    PROCESS_INFORMATION pi;
    ::CreateProcess(NULL,szFileName,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi);
    ::CloseHandle(pi.hThread);
    g_hProcess=pi.hProcess;
    //輸入要修改的值
    int iVal;
    printf("input val=");
    scanf("%d",&iVal);
    FindFirst(iVal);
    //打印出搜索結果
    ShowList();
    while(g_nListCnt>1)
    {
        printf("Input val=");
        scanf("%d",&iVal);
        FindNext(iVal);
        ShowList();
    }
    printf("New value=");
    scanf("%d",&iVal);
    //寫入新值
    if (WriteMemory(g_arList[0],iVal))
    {
        printf("Write data success\n");
    }
    ::CloseHandle(g_hProcess);
    return 0;
}

優點:
1. 通過遍歷內存和更改變量的方式,使得實現起來較爲簡單,邏輯清晰。
缺點:
1. 不能固定住血量對應的變量不變,需要動態的修改血量
2. 對於通過修改變量不能得出唯一地址的程序,該工具失靈
可能的改進:
1. 動態監控血量的變化,如果值低於某個值得時候,自動更改血量
2. 通過反編譯找出相應的指令,修改指令。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章