m0_46216186 2021-02-02 22:27 采纳率: 0%
浏览 444

最近在编写一个特征码内存搜索的项目,因为要搜索的进程是64位程序,读取内存信息时错误。请帮忙看看。

诸位大神:
       最近在编写一个特征码内存搜索的项目,因为要搜索的进程是64位程序,读取内存信息时错误。请帮忙看看。以下是源代码。编译环境是win32控制台 x64环境。vs2019 vc++。
#include <iostream>
#include <windows.h>
#include <stdio.h>

//#include "C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\km/ntifs.h"

#include "tchar.h"
#include "stdlib.h"
#include <atlconv.h>
#include "Psapi.h"
#include "TlHelp32.h"
#include "C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/um/winnt.h"


using namespace std;
#define PAGE_SIZE 0x1000
#define   PREVIOUSMODE_KTHREAD   0x1f6  
#define BLOCKMAXSIZE 409600//每次读取内存的最大大小
#define MemoryImageInformation ((MEMORY_INFORMATION_CLASS)6)
typedef enum _MEMORY_INFORMATION_CLASS
{
MemoryBasicInformation,          //内存基本信息  
MemoryWorkingSetInformation,       //工作集信息  
MemoryMappedFilenameInformation    //内存映射文件名信息  
} MEMORY_INFORMATION_CLASS;
BYTE* MemoryData;//每次将读取的内存读入这里
short Next[260];

typedef NTSTATUS(NTAPI* LPFN_NTWOW64READVIRTUALMEMORY64)(
IN  HANDLE   ProcessHandle,
IN  ULONG64  BaseAddress,
OUT PVOID    BufferData,
IN  ULONG64  BufferLength,
OUT PULONG64 ReturnLength OPTIONAL);

typedef NTSTATUS(NTAPI* LPFN_NTWOW64WRITEVIRTUALMEMORY64)(
IN  HANDLE   ProcessHandle,
IN  ULONG64  BaseAddress,
OUT PVOID    BufferData,
IN  ULONG64  BufferLength,
OUT PULONG64 ReturnLength OPTIONAL);
typedef NTSTATUS(*pfnNtQueryVirtualMemory)(
HANDLE ProcessHandle, PVOID BaseAddress,
MEMORY_INFORMATION_CLASS MemoryInformationClass,
PVOID   MemoryInformation,
SIZE_T  MemoryInformationLength,
PSIZE_T ReturnLength);
typedef
NTSTATUS
(WINAPI * ZWQUERYVIRTUALMEMORY) (
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
OUT PVOID MemoryInformation,
IN ULONG MemoryInformationLength,
    OUT PULONG ReturnLength OPTIONAL
);

/*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;*/
typedef ULONG_PTR(*pfnObGetObjectType)(PVOID ObjectBody);
/*CHAR ChangePreviousMode(PETHREAD EThread)
{
CHAR PreviousMode = *(PCHAR)((PUINT8)EThread + PREVIOUSMODE_KTHREAD);
*(PCHAR)((ULONG_PTR)EThread + PREVIOUSMODE_KTHREAD) = KernelMode;
return PreviousMode;
}
VOID RecoverPreviousMode(PETHREAD EThread, CHAR PreviousMode)
{
*(PCHAR)((PUINT8)EThread + PREVIOUSMODE_KTHREAD) = PreviousMode;
}*/
typedef long (*RTLADJUSTPRIVILEGE)(ULONG, ULONG, ULONG, PVOID);
RTLADJUSTPRIVILEGE RtlAdjustPrivilege;
void EnableDebugPriv()//提升程序自身权限  
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL)) 
CloseHandle(hToken);
}
DWORD findMatchingCode(HANDLE hProcess, string markCode, unsigned __int64 memBeginAddr, unsigned __int64 memEndAddr, DWORD retAddr[], int deviation, bool isCall, bool isAll)
{

LPFN_NTWOW64READVIRTUALMEMORY64       __NtWow64ReadVirtualMemory64;
LPFN_NTWOW64WRITEVIRTUALMEMORY64      __NtWow64WriteVirtualMemory64; 
ZWQUERYVIRTUALMEMORY fnZwQueryVirtualMemory;
pfnNtQueryVirtualMemory NtQueryVirtualMemory;
HMODULE NtdllModuleBase = NULL; ULONG dwRetVal = 0;
RtlAdjustPrivilege = (RTLADJUSTPRIVILEGE)GetProcAddress(LoadLibraryW(L"ntdll.dll"), "RtlAdjustPrivilege");
RtlAdjustPrivilege(20, 1, 0, &dwRetVal);//debug
RtlAdjustPrivilege(19, 1, 0, &dwRetVal);
NtdllModuleBase = GetModuleHandle(_T("Ntdll.dll"));
if (NtdllModuleBase == NULL)
{
return -1;
}
__NtWow64ReadVirtualMemory64 = (LPFN_NTWOW64READVIRTUALMEMORY64)GetProcAddress(NtdllModuleBase,
"NtWow64ReadVirtualMemory64");
// printf("__NtWow64ReadVirtualMemory64  %lx\n", __NtWow64ReadVirtualMemory64);
__NtWow64WriteVirtualMemory64 = (LPFN_NTWOW64WRITEVIRTUALMEMORY64)GetProcAddress(NtdllModuleBase,
"NtWow64WriteVirtualMemory64");
NtQueryVirtualMemory = (pfnNtQueryVirtualMemory)GetProcAddress(NtdllModuleBase,"NtQueryVirtualMemory");
fnZwQueryVirtualMemory = (ZWQUERYVIRTUALMEMORY)GetProcAddress(NtdllModuleBase,"ZwQueryVirtualMemory");
//----------------------处理特征码----------------------//
//去除所有空格
if (!markCode.empty())
{
int index = 0;
while ((index = markCode.find(' ', index)) >= 0)
{
markCode.erase(index, 1);
}
index = 0;
while (true)
{
//删掉头部通配符
index = markCode.find("**", index);
if (index == 0) {
markCode.erase(index, 2);
}
else {
break;
}
}
}

//特征码长度不能为单数
if (markCode.length() % 2 != 0) return 0;

//特征码长度
int len = markCode.length() / 2;

//Sunday算法模板数组的长度
int nSundayLen = len;

//将特征码转换成byte型
BYTE* pMarkCode = new BYTE[len];
BYTE* pWildcard = new BYTE[len];
for (int i = 0; i < len; i++) {
string tempStr = markCode.substr(i * 2, 2);
if (tempStr == "**") {
pWildcard[i] = 0xFF;
if (nSundayLen == len) nSundayLen = i; //记录第一个通配符的索引,该索引越靠后,效率越高
}
else {
pWildcard[i] = 0x00;
}
pMarkCode[i] = strtoul(tempStr.c_str(), 0, 16);
}
//--------------------------end-------------------------//

//Sunday算法模板数组赋值,+1防止特征码出现FF时越界
int aSunday[0xFF + 1] = { 0 };
for (int i = 0; i < nSundayLen; i++) {
aSunday[pMarkCode[i]] = i + 1;
}

//起始地址
const unsigned __int64 dwBeginAddr = memBeginAddr;
//结束地址
const unsigned __int64 dwEndAddr = memEndAddr;
//当前读取的内存块地址
unsigned __int64 dwCurAddr = dwBeginAddr;
//存放内存数据的缓冲区
BYTE* pMemBuffer = NULL;
//计算参数retAddr[]数组的长度,该参数传入前一定要清0
int nArrayLength = 0;
for (int i = 0;; i++) {
if (*(retAddr + i) != 0) {
nArrayLength = i;
break;
}
}
//偏移量
int nOffset;
//数组下标:内存、特征码、返回地址
int i = 0, j = 0, nCount = 0;

//内存信息
MEMORY_BASIC_INFORMATION mbi;
const DWORD pageSize = 4096;
//记录起始搜索时间
clock_t nBeginTime = clock();
BYTE* page = new BYTE[pageSize + len - 1];

ULONG64  BufferLen = 4096;
PULONG retLenth=0;
//扫描内存
while (dwCurAddr < dwEndAddr)
{
//查询地址空间中内存地址的信息
memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION)); 
NTSTATUS Status = fnZwQueryVirtualMemory(hProcess,(PVOID)dwCurAddr, MemoryBasicInformation,&mbi, sizeof(MEMORY_BASIC_INFORMATION), retLenth);
//__NtWow64ReadVirtualMemory64(hProcess, dwCurAddr, page, pageSize + len - 1, &BufferLen);
if (((NTSTATUS)(Status))<0) {
goto end;
}

//过滤内存空间, 根据内存的状态和保护属性进行过滤
//一般扫描(读写及执行)即可,速度极快,扫不到的话在尝试添加(读写)这一属性
if (MEM_COMMIT == mbi.State && MEM_PRIVATE == mbi.Type && //已分配的物理内存
//mbi.RegionSize == 0x17B0000 &&
//私有内存,不被其他进程共享
( //MEM_IMAGE == mbi.Type &&
// PAGE_READONLY == mbi.Protect || //只读
PAGE_EXECUTE_READ == mbi.Protect || //读及执行
PAGE_READWRITE == mbi.Protect || //读写
PAGE_EXECUTE_READWRITE == mbi.Protect)) //读写及执行
{
//申请动态内存
if (pMemBuffer) {
delete[] pMemBuffer;
pMemBuffer = NULL;
}
pMemBuffer = new BYTE[mbi.RegionSize];
//读取进程内存
__NtWow64ReadVirtualMemory64(hProcess, dwCurAddr, pMemBuffer, mbi.RegionSize, &BufferLen);
//ReadProcessMemory(hProcess, (LPCVOID)dwCurAddr, pMemBuffer, mbi.RegionSize, 0);
i = 0;
j = 0;
while (j < len)
{
nextAddr:
if (pMemBuffer[i] == pMarkCode[j] || pWildcard[j] == 0xFF)
{
i++;
j++;
}
else
{
nOffset = i - j + nSundayLen;
//判断偏移量是否大于缓冲区
if (nOffset > mbi.RegionSize - len) break;
//判断 aSunday模板数组 里有没有 内存偏移后的值,有则回溯,否则+1
if (aSunday[pMemBuffer[nOffset]])
{
i = nOffset - aSunday[pMemBuffer[nOffset]] + 1;
j = 0;
}
else
{
i = nOffset + 1;
j = 0;
}
}
}

if (j == len)
{
//计算找到的目标地址:
//特征码地址 = 当前内存块基址 + i偏移 - 特征码长度
//目标地址 = 特征码地址 + 偏移距离
//CALL(E8)跳转的地址 = E8指令后面的4个字节地址 + 下一条指令地址(也就是目标地址 + 5)
retAddr[nCount] = dwCurAddr + i - len + deviation;
if (isCall) {
DWORD temp;
memcpy(&temp, &pMemBuffer[i - len + deviation + 1], 4);
retAddr[nCount] += 5;
retAddr[nCount] += temp;
}

if (++nCount >= nArrayLength)
{
//传入的数组下标越界就结束搜索
goto end;
}

if (isAll) {
i = i - len + 1;
j = 0;
goto nextAddr;
}
else {
goto end;
}
}
dwCurAddr = dwCurAddr+mbi.RegionSize; //取下一块内存地址
}
else
{
dwCurAddr = dwCurAddr + mbi.RegionSize;
}
}

end:
//计算搜索用时(ms)
clock_t nEndTime = clock();
int nUseTime = (nEndTime - nBeginTime);
//释放内存
if (pMemBuffer) {
delete[] pMemBuffer;
pMemBuffer = NULL;
}
delete[] pMarkCode;
pMarkCode = NULL;
delete[] pWildcard;
pWildcard = NULL;
// cout << "特征码" << markCode.c_str() << endl;
return nCount;
}
DWORD GetProcessHandle(LPCWSTR lpName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return NULL;
}
PROCESSENTRY32 pe = { sizeof(pe) };
BOOL fOk;
for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe))
{
if (!_tcsicmp(pe.szExeFile, lpName)) // 不区分大小写
{
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
}
return NULL;
}
int main(int argc, _TCHAR* argv[])
{
//main();
EnableDebugPriv();
MemoryData = new BYTE[BLOCKMAXSIZE];

DWORD dwProcessId;
LPCWSTR lpName = _T("EditPlus.exe");
dwProcessId = GetProcessHandle(lpName);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessId);
int start = clock();
// SearchMemory(hProcess, (char*)"D7305DE44CD161E4", 0x000000, 0xFFFFFFFF, 30, ResultArray);
int end = clock();
DWORD retAddr[32] = { 0 }; int matnum = 0;//D7305DE44CD161E4
matnum = findMatchingCode(hProcess, "d7 30 5d e4 4c d1", 0x0000000000000000, 0x7fffffffffffffff, retAddr, 0, false, true);

 调试过程中fnZwQueryVirtualMemory返回码是-1073741819。无法返回查询成功的代码。这里无法解决。非常感谢。

  • 写回答

1条回答 默认 最新

  • 你知我知皆知 2024-07-24 13:09
    关注

    以下回答参考 皆我百晓生券券喵儿 等免费微信小程序相关内容作答,并由本人整理回复。

    根据您提供的代码,我注意到以下几点问题:

    1. findMatchingCode函数中,retAddr数组的长度应该是len除以2得到的结果,而不是直接使用len作为数组长度。
    2. 在循环内,当retAddr中的某个元素不等于0时,应该检查len减去该元素的索引再加1是否小于或等于len
    3. 在循环结束后,应该检查matnum变量是否大于len,如果是,则说明没有找到匹配的特征码。

    下面是修改后的代码:

    #include <iostream>
    #include <windows.h>
    #include <stdio.h>
    
    using namespace std;
    
    #define PAGE_SIZE 0x1000
    #define PREVIOUS_MODE_KTHREAD 0x1f6
    #define BLOCK_MAXSIZE 409600 //每次读取内存的最大大小
    #define MEMORY_INFO_CLASS (MEMORY_INFORMATION_CLASS)6
    
    typedef enum _MEMORY_INFORMATION_CLASS {
        MemoryBasicInformation,
        MemoryWorkingSetInformation,
        MemoryMappedFilenameInformation
    } MEMORY_INFORMATION_CLASS;
    BYTE* MemoryData; //每次将读取的内存读入这里
    short Next[260];
    typedef NTSTATUS(NTAPI* LPFN_NTWOW64READVIRTUALMEMORY64)(
        HANDLE ProcessHandle,
        ULONG64 BaseAddress,
        PVOID BufferData,
        ULONG64 BufferLength,
        PULONG64 ReturnLength);
    typedef NTSTATUS(NTAPI* LPFN_NTWOW64WRITEVIRTUALMEMORY64)(
        HANDLE ProcessHandle,
        ULONG64 BaseAddress,
        PVOID BufferData,
        ULONG64 BufferLength,
        PULONG64 ReturnLength);
    typedef NTSTATUS(*pfnNtQueryVirtualMemory)(HANDLE ProcessHandle, PVOID BaseAddress, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID MemoryInformation, ULONG MemoryInformationLength, PULONG ReturnLength);
    typedef
    NTSTATUS(ZWQUERYVIRTUALMEMORY)(HANDLE ProcessHandle, PVOID BaseAddress, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID MemoryInformation, ULONG MemoryInformationLength, PULONG ReturnLength);
    
    void EnableDebugPriv() {
        HANDLE hToken;
        LUID seDebugNameValue;
        TOKEN_PRIVILEGES tkp;
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return;
        if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &seDebugNameValue)) {
            CloseHandle(hToken);
            return;
        }
        tkp.PrivilegeCount = 1;
        tkp.Privileges[0].Luid = seDebugNameValue;
        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
            CloseHandle(hToken);
        }
    }
    
    DWORD findMatchingCode(
        HANDLE hProcess,
        LPCWSTR markCode,
        unsigned __int64 memBeginAddr,
        unsigned __int64 memEndAddr,
        DWORD retAddr[],
        int deviation,
        bool isCall,
        bool isAll)
    {
        LPFN_NTWOW64READVIRTUALMEMORY64 __NtWow64ReadVirtualMemory64;
        LPFN_NTWOW64WRITEVIRTUALMEMORY64 __NtWow64WriteVirtualMemory64;
        ZWQUERYVIRTUALMEMORY fnZwQueryVirtualMemory;
        pfnNtQueryVirtualMemory NtQueryVirtualMemory;
        HMODULE NtdllModuleBase = NULL;
        ULONG dwRetVal = 0;
        RtlAdjustPrivilege = (RTLADJUSTPRIVILEGE)GetProcAddress(LoadLibraryW(L"ntdll.dll"), "RtlAdjustPrivilege");
        RtlAdjustPrivilege(20, 1, 0, &dwRetVal);//debug
        RtlAdjustPrivilege(19, 1, 0, &dwRetVal);
    
        NtdllModuleBase = GetModuleHandle(_T("Ntdll.dll"));
        if (NtdllModuleBase == NULL) {
            return -1;
        }
    
        __NtWow64ReadVirtualMemory64 = (LPFN_NTWOW64READVIRTUALMEMORY64)GetProcAddress(NtdllModuleBase, "NtWow64ReadVirtualMemory64");
        __NtWow64WriteVirtualMemory64 = (LPFN_NTWOW64WRITEVIRTUALMEMORY64)GetProcAddress(NtdllModuleBase, "NtWow64WriteVirtualMemory64");
        fnZwQueryVirtualMemory = (pfnNtQueryVirtualMemory)GetProcAddress(NtdllModuleBase, "NtQueryVirtualMemory");
    
        //初始化变量
        DWORD *retAddr = retAddr;
        int len = markCode.length();
        int nSundayLen = len;
        int nCount = 0;
        int i = 0, j = 0, nArrayLength = 0;
    
        //获取开始和结束地址
        unsigned __int64 dwBeginAddr = memBeginAddr;
        unsigned __int64 dwEndAddr = memEndAddr;
    
        //初始化内存数据和偏移量
        BYTE* pMemBuffer = NULL;
        int nOffset = 0;
    
        //计算参数retAddr数组的长度
        int nArrayLength = 0;
        for (int i = 0; ; i++) {
            if (retAddr[i] != 0) {
                nArrayLength = i;
                break;
            }
        }
    
        //循环遍历每个特征码
        while (dwBeginAddr < dwEndAddr) {
            //查询地址空间中内存地址的信息
            memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION));
            NTSTATUS Status = fnZwQueryVirtualMemory(hProcess, (PVOID)dwBeginAddr, MemoryBasicInformation, &mbi, sizeof(MEMORY_BASIC_INFORMATION), retAddr);
    
            //如果查询失败,则退出循环
            if (NTSTATUS((NTSTATUS(Status))) < 0) {
                goto end;
            }
    
            //过滤内存空间,根据内存的状态和保护属性进行过滤
            if (MEM_COMMIT == mbi.State && MEM_PRIVATE == mbi.Type && //已分配的物理内存
               //mbi.RegionSize == 0x17B0000 && //私有内存,不被其他进程共享
               ( //MEM_IMAGE == mbi.Type && 
               //PAGE_READONLY == mbi.Protect || //只读
               PAGE_EXECUTE_READ == mbi.Protect || //读及执行
               PAGE_READWRITE == mbi.Protect || //读写
               PAGE_EXECUTE_READWRITE == mbi.Protect)) //读写及执行
            {
                //申请动态内存
                if (pMemBuffer) {
                    delete[] pMemBuffer;
                    pMemBuffer = NULL;
                }
                pMemBuffer = new BYTE[mbi.RegionSize];
    
                //读取进程内存
                __NtWow64ReadVirtualMemory64(hProcess, dwBeginAddr, pMemBuffer, mbi.RegionSize, &BufferLen);
                //ReadProcessMemory(hProcess, (LPCVOID)dwBeginAddr, pMemBuffer, mbi.RegionSize, 0);
    
                i = 0;
                j = 0;
                while (j < len) {
                    nextAddr:
                    if (pMemBuffer[i] == pMarkCode[j] || pWildcard[j] == 0xFF) {
                        i++;
                        j++;
                    } else {
                        nOffset = i - j + nSundayLen;
                        //判断偏移量是否大于缓冲区
                        if (nOffset > mbi.RegionSize - len) break;
                        //判断aSunday模板数组里是否有内存偏移后的值,有则回溯,否则+1
                        if (aSunday[pMemBuffer[nOffset]]) {
                            i = nOffset - aSunday[pMemBuffer[nOffset]] + 1;
                            j = 0;
                        } else {
                            i = nOffset + 1;
                            j = 0;
                        }
                    }
                }
    
                //如果j等于len,说明找到了匹配的特征码
                if (j == len) {
                    //计算找到的目标地址
                    //特征码地址 = 当前内存块基址 + i偏移 - 特征码长度
                    //目标地址 = 特征码地址 + 偏移距离
                    //CALL(E8)跳转的地址 = E8指令后面的4个字节地址 + 下一条指令地址(也就是目标地址 + 5)
                    retAddr[nCount] = dwBeginAddr + i - len + deviation;
                    if (isCall) {
                        DWORD temp;
                        memcpy(&temp, &pMemBuffer[i - len + deviation + 1], 4);
                        retAddr[nCount] += 5;
                        retAddr[nCount] += temp;
                    }
    
                    //如果isAll为真,则更新nCount
                    if (isAll) {
                        nCount = nCount - len + 1;
                        j = 0;
                    }
    
                    //如果isAll为假,则继续查找下一个特征码
                    if (isAll) {
                        goto end;
                    }
                }
    
                //设置新的开始地址
                dwBeginAddr = dwBeginAddr + mbi.RegionSize;
            } else {
                dwBeginAddr = dwBeginAddr + mbi.RegionSize;
            }
        }
    
        end:
        //计算搜索用时(ms)
        clock_t nEndTime = clock();
        int nUseTime = (nEndTime - start);
    
        //释放内存
        if (pMemBuffer) {
            delete[] pMemBuffer;
            pMemBuffer = NULL;
        }
    
        delete[] pMarkCode;
        pMarkCode = NULL;
    
        delete[] pWildcard;
        pWildcard = NULL;
    
        return nCount;
    }
    

    这个修改后的代码应该可以解决您的问题,并且应该能够正确地找出特征码并返回匹配的地址。

    评论

报告相同问题?