2

I am attempting to retrieve every occurrence of a certain string, say "ExampleString". What I currently have will find the first occurrence of the string in a process's memory but it won't find the subsequent strings. I tried to use a vector to store all the results but it only finds one result still.

Below is the function I am using to get the vector of memory locations. Again, it works for the first location.

std::vector<char*> GetAddressOfData(DWORD pid, const char *data, size_t len) {
    HANDLE process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);

    std::vector<char*> locations;
    int cur = 0;
    if(process){
        SYSTEM_INFO si;
        GetSystemInfo(&si);

        MEMORY_BASIC_INFORMATION info;
        std::vector<char> chunk;
        char* p = 0;
        while(p < si.lpMaximumApplicationAddress){
            if(VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info)){
                p = (char*)info.BaseAddress;
                chunk.resize(info.RegionSize);
                SIZE_T bytesRead;
                if(ReadProcessMemory(process, p, &chunk[0], info.RegionSize, &bytesRead)){
                    for(size_t i = 0; i < (bytesRead - len); ++i){
                        if(memcmp(data, &chunk[i], len) == 0) {
                            cur++;
                            locations.resize(cur);
                            locations[cur-1] = (char*)p + i;
                            std::cout << "Found*: " << (void*)locations[cur-1] << "\n";
                        }
                    }
                }
                p += info.RegionSize;
            }
        }
    }
    return locations;
}
Chris
  • 2,435
  • 6
  • 26
  • 49

1 Answers1

5

Here's some code I wrote years ago to do essentially what you're asking for:

#include <iostream>
#include <vector>
#include <string>
#include <windows.h>
#include <algorithm>
#include <iterator>

template <class InIter1, class InIter2, class OutIter>
void find_all(unsigned char *base, InIter1 buf_start, InIter1 buf_end, InIter2 pat_start, InIter2 pat_end, OutIter res) {
    for (InIter1 pos = buf_start;
        buf_end!=(pos=std::search(pos, buf_end, pat_start, pat_end));
        ++pos)
    {
        *res++ = base+(pos-buf_start);
    }
}

template <class outIter>
void find_locs(HANDLE process, std::string const &pattern, outIter output) {

    unsigned char *p = NULL;
    MEMORY_BASIC_INFORMATION info;

    for ( p = NULL;
        VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info);
        p += info.RegionSize ) 
    {
        std::vector<char> buffer;
        std::vector<char>::iterator pos;

        if (info.State == MEM_COMMIT && 
            (info.Type == MEM_MAPPED || info.Type == MEM_PRIVATE)) 
        {
            SIZE_T bytes_read;
            buffer.resize(info.RegionSize);
            ReadProcessMemory(process, p, &buffer[0], info.RegionSize, &bytes_read);
            buffer.resize(bytes_read);
            find_all(p, buffer.begin(), buffer.end(), pattern.begin(), pattern.end(), output);
        }
    }
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <process ID> <pattern>", argv[0]);
        return 1;
    }

    int pid;
    sscanf(argv[1], "%i", &pid);

    std::string pattern(argv[2]);

    HANDLE process = OpenProcess( 
        PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, 
        false,
        pid);

    find_locs(process, pattern, 
        std::ostream_iterator<void *>(std::cout, "\n"));

    return 0;
}

I do think it's worth noting that it looks like your code has most of the same general ideas as this does--the differences are primarily in details that probably look pretty trivial, at least at first glance (but I guess that's not surprising, given that your code is close enough to at least find an initial occurrence).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Bonus for using std::search which probably searches smarter than memcmp() on every step. – thomasa88 Jul 04 '21 at 06:31
  • Thank you for the code, however this doesn't find anything for me. – Mecanik Oct 17 '22 at 08:48
  • Well, to be more specific... it works for a single character but not a whole string. For example "@". – Mecanik Oct 17 '22 at 08:59
  • @Mecanik: not sure where you've gone wrong, but I've used this code (successfully) to search for entire strings. – Jerry Coffin Oct 17 '22 at 14:38
  • @JerryCoffin Now that makes me very sad. I literally copy and pasted your whole code and searched for the string. Nothing found unless is a single character :(. On another note... maybe you could help me print all strings? Using your method/code of course. – Mecanik Oct 17 '22 at 15:13
  • 1
    @Mecanik: Printing all strings would require rather different code. Much of it would be devoted to deciding whether a specific sequence of characters constitutes a "string" or not. As for not finding something: I'd *guess* the target you're looking at is compiled as a Unicode application. If so, a string like `foo` will be stored in memory something like `"f\0o\0o\0"`, so if you search for `"foo"`, it won't be found--not because of anything wrong with this code, but because the string you've asked it to search for actually doesn't exist in that target. – Jerry Coffin Oct 17 '22 at 16:55
  • @JerryCoffin I see, well that would have been nice anyway. As for UNICODE, indeed, I forgot to mention it's UNICODE... – Mecanik Oct 17 '22 at 16:57
  • @JerryCoffin I believe from what I see std::vector::iterator pos; is doing nothing there. Also, after changing to wstring I can see some addresses being printed, which is great news! Could I use a vector instead of the console output? I wish to iterate these addresses and read them to see the whole content where the string is. – Mecanik Oct 17 '22 at 17:05
  • @Mecanik: yes, changing to putting results in a vector should be fairly straightforward. – Jerry Coffin Oct 17 '22 at 17:34
  • @JerryCoffin Thanks, turns out it's not. Having a headache with it :( – Mecanik Oct 17 '22 at 17:35
  • @Mecanik: I'd change the call to `find_locs` to something like this: `std::vector locations; find_locs(process, pattern, std::back_inserter(locations));` – Jerry Coffin Oct 17 '22 at 17:52
  • @JerryCoffin Thanks a lot, I was on the right way but forgot about std::back_inserter. Is there a way to get the region size since I want to read the location of the results? Or should I just use a default number like 256? – Mecanik Oct 17 '22 at 17:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248861/discussion-between-mecanik-and-jerry-coffin). – Mecanik Oct 17 '22 at 18:10