#include <vector>
#include <Windows.h>
#include <iostream>
#include <sstream>
#include <string>
​
//字符串转换成整数表
std::vector<int> BuildSkipTable(const std::vector<int>& pattern) {
    const int wildcard = -1;
    //最大支持字符数
    const int tableSize = 256;
    std::vector<int> table(tableSize, pattern.size());
​
    for (int i = 0; i < pattern.size() - 1; i++) {
        if (pattern[i] != wildcard) {
            table[pattern[i]] = pattern.size() - 1 - i;
        }
    }
​
    return table;
}
​
//解析字符串特征码
std::vector<int> ParsePattern(const std::string& pattern) {
    std::vector<int> parsedPattern;
    std::stringstream ss(pattern);
    std::string byte;
​
    while (std::getline(ss, byte, ' ')) {
        //过滤通配符  目前支持 "?" "??" "*"
        if (byte == "?" || byte == "*" || byte == "??") {
            parsedPattern.push_back(-1);
        }
        else {
            parsedPattern.push_back(std::stoi(byte, 0, 16));
        }
    }
​
    return parsedPattern;
}
​
std::vector<SIZE_T> BMH_AOBScanWithWildcard(HANDLE process, const std::vector<int>& pattern) {
    std::vector<SIZE_T> results;
    MEMORY_BASIC_INFORMATION mbi = { 0 };
    SIZE_T size = 0;
    std::vector<int> skipTable = BuildSkipTable(pattern);
​
    while (VirtualQueryEx(process, (LPCVOID)size, &mbi, sizeof(mbi))) {
        //内存页属性要求要有可执行属性,否则会被被过滤掉    可以根据需求调整
        if (mbi.State == MEM_COMMIT && (mbi.Protect >= PAGE_EXECUTE && mbi.Protect <= PAGE_EXECUTE_WRITECOPY)) {
            BYTE* buffer = new BYTE[mbi.RegionSize];
            SIZE_T bytesRead;
​
            if (ReadProcessMemory(process, mbi.BaseAddress, buffer, mbi.RegionSize, &bytesRead)) {
                BYTE* begin = buffer;
                BYTE* end = buffer + bytesRead;
                int m = pattern.size();
​
                for (BYTE* i = begin; i <= end - m;) {
                    int j = m - 1;
                    while (j >= 0 && (pattern[j] == -1 || pattern[j] == i[j])) {
                        --j;
                    }
                    if (j < 0) {
                        results.push_back((SIZE_T)((BYTE*)mbi.BaseAddress + (i - begin)));
                        i += m;
                    }
                    else {
                        i += skipTable[i[m - 1]];
                    }
                }
            }
​
            delete[] buffer;
        }
​
        size += mbi.RegionSize;
    }
​
    return results;
}
​
​
#include <tchar.h>
#include <tlhelp32.h>
#pragma comment(lib, "kernel32.lib")
​
//通过进程名字获取PID
DWORD GetProcessIdByName(const TCHAR* processName){
    DWORD processId = 0;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
​
    if (hSnapshot != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(PROCESSENTRY32);
​
        if (Process32First(hSnapshot, &pe32)) {
            do {
                if (_tcscmp(pe32.szExeFile, processName) == 0) {
                    processId = pe32.th32ProcessID;
                    break;
                }
            } while (Process32Next(hSnapshot, &pe32));
        }
​
        CloseHandle(hSnapshot);
    }
​
    return processId;
}
​
​

示例代码:

int main() {
    // 目标进程的名字,获取PID
    SIZE_T processId = GetProcessIdByName(L"WS-Win64-Shipping.exe"); 
    //特征码
    std::string pattern = "48 83 C1 10 E8 9D EC 42 FF 48 8D 57 20 48 8D 4B 20 E8 90 EC 42 FF 48 8D 57 30";
​
    HANDLE process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId);
    if (process != NULL) {
        std::vector<int> parsedPattern = ParsePattern(pattern);
        std::vector<SIZE_T> results = BMH_AOBScanWithWildcard(process, parsedPattern);
​
        for (SIZE_T address : results) {
            std::cout << "Found pattern at: " << std::hex << address << std::endl;
        }
​
        CloseHandle(process);
    }
    else {
        std::cerr << "Failed to open process: " << GetLastError() << std::endl;
    }
​
    return 0;
}

代码使用注意事项:

1.目前代码只会查询带可执行属性的内存页,可以根据自己的需求进行修改。

2.目前代码只支持 “?” "??" "*",类型的通配符,可以根据自己需求添加。(通配符需要使用英文字符

3.特征码每个字节需要用 空格分隔

4.最大支持256个字符,可以根据实际需求更改。