How to (fastly) scan memory?

Hello everyone!

I need to write a function that searches the memory of a process (I'm injected to it, with a DLL) for a specific array of bytes. I wonder the best (i.e. fastest) way to do that.

I've done searches in files before, and it was really fast, but my memory searches are extremely slow.I basically get the starting address using GetModuleHandle() and implement a loop with a lot of VirtualQuerys, ignoring memory pages I don't want (I only want read/write memory), and jumping directly to the next page if I ignore it (I think I made it wrong, but anyway...). So I tried to save the entire memory of the process into a file, but I failed to do so. I've to say that my C++ programming skills are bad, since it's just a hobby for me.

This code is some scratch I tested. How can I make something better?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DWORD base=(DWORD)GetModuleHandle("Minesweeper.exe");
DWORD scanning=base;
BYTE scanned;
MEMORY_BASIC_INFORMATION mbi;
while(1)
{
if(VirtualQuery((LPVOID)scanning,&mbi,sizeof(mbi))!=0)
{
if(mbi.Protect&PAGE_READWRITE)
{
memcpy(&scanned,(LPVOID)scanning,1);
if(scanned==1)
{         
//please don't laugh...
}
if(scanning==0xffffffff){MessageBox(NULL,"Nothing found, scanning again in 3...2...","!",MB_OK);scanning=base-1;}
}else{scanning=scanning+mbi.RegionSize-1;}
}
scanning++;
}




So my question is: How can I create such a fast memory scan? Is it best to save the whole memory into a file and then search this file, or search directly in memory? (the array of bytes I'm searching for doesn't change)


Examples would be greatly appreciated!
I told you before. It doesn't make sense for a process to scan its own memory.

This code dumps the entire address space of another process (selected by PID) to a flat file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include <Windows.h>
#include <iostream>
#include <iomanip>
#include <exception>
#include <cstdint>
#include <vector>
#include <sstream>
#include <fstream>

template <typename T>
void print_hex(std::ostream &stream, T x, int width = 8){
	stream << std::hex << std::setw(width) << std::setfill('0') << x << std::dec;
}

template <typename T>
void print_address(std::ostream &stream, T x){
	if (x < 0x100)
		print_hex(stream, x, 2);
	else if (x < 0x10000)
		print_hex(stream, x, 4);
	else if (x < 0x100000000ULL)
		print_hex(stream, x, 8);
	else
		print_hex(stream, x, 16);
}

class DebugProcess{
	DWORD pid;
public:
	DebugProcess(DWORD pid): pid(pid){
		if (!DebugActiveProcess(pid)){
			auto error = GetLastError();
			std::cerr << "DebugActiveProcess() failed with error " << error << " (0x";
			print_hex(std::cerr, error);
			std::cerr << ")\n";
			throw std::exception();
		}
	}
	~DebugProcess(){
		if (!DebugActiveProcessStop(this->pid)){
			auto error = GetLastError();
			std::cerr << "DebugActiveProcessStop() failed with error " << error << " (0x";
			print_hex(std::cerr, error);
			std::cerr << ")\n";
		}
	}
};

bool is_handle_valid(HANDLE handle){
	return handle && handle != INVALID_HANDLE_VALUE;
}

class AutoHandle{
	HANDLE handle;
public:
	AutoHandle(HANDLE handle): handle(handle){}
	~AutoHandle(){
		if (is_handle_valid(this->handle))
			CloseHandle(this->handle);
	}
};

template <typename T>
void zero_struct(T &mem){
	memset(&mem, 0, sizeof(mem));
}

struct memory_region{
	std::uint64_t start,
		size;
	MEMORY_BASIC_INFORMATION info;
};

void dump_process_memory(DWORD pid){
	DebugProcess dp(pid);

	auto proc = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
	if (!is_handle_valid(proc)){
		auto error = GetLastError();
		std::cerr << "OpenProcess() failed with error " << error << " (0x";
		print_hex(std::cerr, error);
		std::cerr << ")\n";
		return;
	}
	AutoHandle autoproc(proc);

	std::vector<memory_region> regions;
	for (std::uint64_t address = 0; address < 0x10000000ULL;){
		MEMORY_BASIC_INFORMATION mbi;
		zero_struct(mbi);
		auto bytes = VirtualQueryEx(proc, (LPCVOID)address, &mbi, sizeof(mbi));
		if (!bytes){
			address += 4096;
			continue;
		}
		if (mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_GUARD) != PAGE_GUARD)
			regions.push_back(memory_region{ (std::uint64_t)mbi.BaseAddress, mbi.RegionSize, mbi });

		address += mbi.RegionSize;
	}

	if (regions.size()){
		std::cout << "Flat size:   " << regions.back().start + regions.back().size << std::endl;
		std::uint64_t sum = 0;
		for (auto &region : regions)
			sum += region.size;
		std::cout << "Packed size: " << sum << std::endl;
	}

	std::ofstream file("dump.bin", std::ios::binary);
	std::uint64_t current_size = 0;
	for (auto &region : regions){
		std::vector<char> buffer(region.size);
		size_t read;
		if (!ReadProcessMemory(proc, (LPCVOID)region.start, &buffer[0], buffer.size(), &read)){
			auto error = GetLastError();
			if (error != ERROR_PARTIAL_COPY){
				std::cerr << "ReadProcessMemory() failed with error " << error << " (0x";
				print_hex(std::cerr, error);
				std::cerr << ")\n";
				return;
			}
		}

		if (read < region.size){
#if 1
			std::cerr << "Warning: region starting at 0x";
			print_address(std::cerr, region.start);
			std::cerr << " has size " << region.size << ", but only " << read
				<< " bytes could be read by ReadProcessMemory().\n";
#endif
			memset(&buffer[read], 0, buffer.size() - read);
		}

		file.seekp(region.start);

		file.write(&buffer[0], buffer.size());
	}
}

int main(int argc, char **argv){
	if (argc < 2)
		return 0;
	DWORD pid;
	{
		std::stringstream stream(argv[1]);
		if (!(stream >> pid))
			return 0;
	}

	try{
		dump_process_memory(pid);
	}catch (std::exception &){
		std::cerr << "Exception caught.\n";
	}

	return 0;
}
Last edited on
I tried to compile this code, but I got tons of errors...
Get a better compiler.
> Is it best to save the whole memory into a file and then search this file, or search directly in memory?

Searching directly in memory would be incomparably faster.
There is no need to copy the bytes to a secondary memory buffer.

Something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
std::vector<const void*> scan_memory( void* address_low, std::size_t nbytes,
                                      const std::vector<BYTE>& bytes_to_find )
{
    std::vector<const void*> addresses_found ;

    // all readable pages: adjust this as required
    const DWORD pmask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE |
        PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY ;

    ::MEMORY_BASIC_INFORMATION mbi {};

    BYTE* address = static_cast<BYTE*>( address_low ) ;
    BYTE* address_high = address + nbytes ;

    while( address < address_high && ::VirtualQuery( address, std::addressof(mbi), sizeof(mbi) ) )
    {
        // committed memory, readable, wont raise exception guard page
        // if( (mbi.State==MEM_COMMIT) && (mbi.Protect|pmask) && !(mbi.Protect&PAGE_GUARD) )
        if( (mbi.State==MEM_COMMIT) && (mbi.Protect&pmask) && !(mbi.Protect&PAGE_GUARD) )
        {
            const BYTE* begin = static_cast<const BYTE*>(mbi.BaseAddress) ;
            const BYTE* end =  begin + mbi.RegionSize ;

            const BYTE* found = std::search( begin, end, bytes_to_find.begin(), bytes_to_find.end() ) ;
            while( found != end )
            {
                addresses_found.push_back( found ) ;
                found = std::search( found+1, end, bytes_to_find.begin(), bytes_to_find.end() ) ;
            }
        }

        address += mbi.RegionSize ;
        mbi = {} ;
    }

    return addresses_found ;
}

std::vector<const void*> scan_memory( std::string module_name, const std::vector<BYTE>& bytes_to_find )
{
    auto base = ::GetModuleHandleA( module_name.c_str() ) ;
    if( base == nullptr ) return {} ;

    MODULEINFO minfo {} ;
    ::GetModuleInformation( GetCurrentProcess(), base, std::addressof( minfo ), sizeof( minfo ) ) ;
    return scan_memory( base, minfo.SizeOfImage, bytes_to_find ) ;
}

http://rextester.com/YFCKE98983
Last edited on
What are you doing on line 18? mbi.Protect|pmask is always true.
Yes, typo (corrected now). Should have been (mbi.Protect&pmask)

Thank you!
Topic archived. No new replies allowed.