Как вернуть имя переменной, хранящейся по определенному адресу памяти в C ++ - PullRequest
1 голос
/ 24 августа 2010

при первой публикации здесь после того, как многие из моих результатов Google появились на этом замечательном сайте.

По сути, я хотел бы найти имя переменной, хранящейся по определенному адресу памяти.У меня есть приложение для редактирования памяти, которое я написал, которое редактирует одно значение, проблема в том, что каждый раз, когда приложение, содержащее это значение, исправляется, мне приходится жестко кодировать новый адрес памяти в своем приложении и перекомпилировать, что занимает так много времени, чтоследите за тем, чтобы это почти не стоило делать.

Что я хотел бы сделать, это взять имя переменной, хранящейся по определенному адресу памяти, чтобы я мог затем найти ее адрес во время выполнения и использовать его какадрес памяти для редактирования.

Это все написано на C ++.

Заранее спасибо!

Редактировать:

Ну, я решил, что яЯ хотел бы передать данные из файла .txt, но я не уверен, как преобразовать строку в LPVOID для использования в качестве адреса памяти в WriteProcessMemory ().Это то, что я пробовал:

    string fileContents;

    ifstream memFile("mem_address.txt");
        getline(memFile, fileContents);
    memFile.close();

    LPVOID memAddress = (LPVOID)fileContents.c_str();

    //Lots of code..

    WriteProcessMemory(WindowsProcessHandle, memAddress, &BytesToBeWrote, sizeof(BytesToBeWrote), &NumBytesWrote);

Код все правильно с точки зрения синтаксиса, он компилируется и запускается, но ошибки WriteProcessMemory и я могу только представить, что это связано с моей ошибочной переменной LPVOID,Я прошу прощения, если расширение использования моего вопроса противоречит правилам, я удалю свою правку, если это так.

Ответы [ 7 ]

7 голосов
/ 24 августа 2010

Скомпилируйте и сгенерируйте так называемый map файл.Это можно легко сделать с помощью Visual-C ++ (опция компоновщика /MAP).Там вы увидите символы (функции, ...) с их начальным адресом.Используя этот файл карты (Внимание: его нужно обновлять при каждой перекомпиляции), вы можете сопоставлять адреса с именами.

На самом деле это не так просто, потому что адреса относятся к предпочтительному адресу загрузки и, вероятно,(рандомизация) отличается от фактического адреса загрузки.

Некоторые старые подсказки по поиску правильного адреса можно найти здесь: http://home.hiwaay.net/~georgech/WhitePapers/MapFiles/MapFiles.htm

3 голосов
/ 24 августа 2010

Как правило, имена переменных не сохраняются при компиляции программы. Если вы контролируете процесс компиляции, вы обычно можете настроить компоновщик и компилятор для создания файла карты, в котором перечислены местоположения в памяти всех глобальных переменных. Однако, если это так, вы, вероятно, сможете легче достичь своих целей, не используя прямой доступ к памяти, а скорее создав надлежащий протокол команд, в который может обращаться ваша внешняя программа.

Если у вас нет контроля над процессом компиляции другой программы, вам, вероятно, не повезет, если только программа не поставляется с файлом карты или символами отладки, которые можно использовать для получения имен переменных с их адресов.

Обратите внимание, что для стековых переменных получение их имен потребует полных символов отладки и является очень нетривиальным процессом. Переменные кучи не имеют имен, поэтому вам, конечно, не повезет. Кроме того, как упоминалось в ответе @ jdehaan, файлы карт могут быть немного сложными для работы в лучшие времена. В общем, лучше иметь надлежащий протокол управления, который вы можете использовать, чтобы вообще не зависеть от содержимого памяти другой программы.

Наконец, если у вас нет контроля над другой программой, я бы порекомендовал поместить переменную в отдельный файл данных. Таким образом, вам больше не нужно будет каждый раз перекомпилировать, и вы даже можете поддерживать несколько версий программы, в которую выкалываете. У вас также может быть какая-то служба автообновления, получающая новые версии этого файла данных с вашего сервера, если хотите.

1 голос
/ 24 августа 2010

Вы не можете напрямую сделать это; имена переменных на самом деле не существуют в скомпилированном двоичном файле. Вы могли бы сделать это, если бы программа была написана, скажем, на Java или C #, которые хранят информацию о переменных в скомпилированном двоичном файле.

Кроме того, в общем случае это невозможно, поскольку всегда возможно, чтобы самая последняя копия значения внутри целевой программы находилась внутри регистра ЦП, а не в памяти. Это более вероятно, если рассматриваемая программа скомпилирована в режиме выпуска с включенной оптимизацией.

Если вы можете быть уверены, что целевая программа скомпилирована в режиме отладки, вы сможете использовать символы отладки, генерируемые компилятором (файл .pdb), для сопоставления адресов с переменными, но в этом случае вам потребуется запустить целевой процесс, как если бы он был отлажен - обычные методы чтения памяти процесса и записи памяти процесса не будут работать.

Наконец, ваш вопрос игнорирует очень важное соображение - не обязательно, чтобы переменная соответствовала определенному адресу, даже если такая информация хранится.

1 голос
/ 24 августа 2010

Если вы не являетесь владельцем рассматриваемого приложения, стандартного способа сделать это не существует.Если вы являетесь владельцем приложения, вы можете следить за ответом @jdehaan.

В любом случае, вместо жесткого кодирования адреса памяти в вашем приложении, почему бы не разместить простой канал, который вы можете обновить в любое время с помощьюадрес памяти нужно менять для каждой версии целевого приложения?Таким образом, вместо того, чтобы перекомпилировать ваше приложение каждый раз, вы можете просто обновить этот фид, когда вам понадобится манипулировать новой версией.

0 голосов
/ 24 августа 2010

WinDBG имеет особенно полезную команду

здесь

Учитывая местоположение в памяти, он даст название символа в этом месте. С правильной информацией отладки это благо отладчика (я имею в виду человека, занимающегося отладкой :))!.

Вот пример вывода на моей системе (XP SP3)

0: 000> ln 7c90e514 (7c90e514)
ntdll! KiFastSystemCallRet | (7c90e520) ntdll! KiIntSystemCall Точные совпадения: ntdll! KiFastSystemCallRet ()

0 голосов
/ 24 августа 2010

Билли О'Нил начал двигаться в правильном направлении, но не смог (IMO) достичь реальной цели. Предполагая, что ваша цель - Windows, гораздо более простым способом было бы использовать функции-обработчики символов Windows, в частности SymFromName, которые позволят вам указать имя символа, и он вернет (среди прочего) адрес этого символа.

Конечно, чтобы сделать все это, вам нужно будет работать под учетной записью, которой разрешено выполнять отладку. Однако, по крайней мере, для глобальных переменных вам не обязательно останавливать целевой процесс, чтобы найти символы, адреса и т. Д. На самом деле, он отлично работает, когда процесс использует их сам, если он того пожелает (довольно лишь немногие из моих ранних экспериментов, познакомившихся с этими функциями, сделали именно это). Вот небольшой демонстрационный код, который я написал несколько лет назад, который дает хотя бы общее представление (хотя он достаточно стар, чтобы использовать SymGetSymbolFromName, что на пару поколений позади SymFromName). Скомпилируйте его с отладочной информацией и отойдите - он выдает довольно много.

#define UNICODE
#define _UNICODE
#define DBGHELP_TRANSLATE_TCHAR
#include <windows.h>
#include <imagehlp.h>
#include <iostream>
#include <ctype.h>
#include <iomanip>
#pragma comment(lib, "dbghelp.lib")

int y;

int junk() {
    return 0;
}

struct XXX { 
    int a;
    int b;
} xxx;

BOOL CALLBACK 
sym_handler(wchar_t const *name, ULONG_PTR address, ULONG size, void *) {
    if (name[0] != L'_')
        std::wcout << std::setw(40) << name 
            << std::setw(15) << std::hex << address 
            << std::setw(10) << std::dec << size << L"\n";
    return TRUE;
}

int 
main() {
    char const *names[] = { "y", "xxx"};

    IMAGEHLP_SYMBOL info;

    SymInitializeW(GetCurrentProcess(), NULL, TRUE);

    SymSetOptions(SYMOPT_UNDNAME);

    SymEnumerateSymbolsW(GetCurrentProcess(), 
        (ULONG64)GetModuleHandle(NULL),
        sym_handler,
        NULL);

    info.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);

    for (int i=0; i<sizeof(names)/sizeof(names[0]); i++) {
        if ( !SymGetSymFromName(GetCurrentProcess(), names[i], &info)) {
            std::wcerr << L"Couldn't find symbol 'y'";
            return 1;
        }

        std::wcout << names[i] << L" is at: " << std::hex << info.Address << L"\n";
    }

    SymCleanup(GetCurrentProcess());
    return 0;
}
0 голосов
/ 24 августа 2010

Если у вас есть источник для рассматриваемого приложения, и оптимальное использование памяти не имеет значения, тогда вы можете объявить интересные переменные внутри дружественной отладке структуры, подобной:

typedef struct {
    const char head_tag[15] = "VARIABLE_START";
          char var_name[32];
          int  value;
    const char tail_tag[13] = "VARIABLE_END";
} debuggable_int;

Теперь вашПриложение должно иметь возможность осуществлять поиск в памяти программы и искать теги головы и хвоста.Найдя одну из ваших отлаживаемых переменных, он может использовать члены var_name и value для ее идентификации и изменения.

Однако, если вы собираетесь перейти к этой длине, вы, вероятно, будетелучше строить с включенными символами отладки и обычным отладчиком.

...