簡體   English   中英

如何在C ++中返回存儲在特定內存地址的變量的名稱

[英]How to return the name of a variable stored at a particular memory address in C++

在我獲得了如此多的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變量有關。 抱歉,如果我擴大問題的使用范圍是違反規則的,我將予以刪除。

編譯並生成一個所謂的map文件。 使用Visual-C ++( /MAP鏈接器選項)可以輕松完成此操作。 在這里,您將看到符號(功能,...)及其起始地址。 使用此映射文件(警告:每次重新編譯時都必須更新),您可以將地址與名稱匹配。

實際上,這並不是那么容易,因為地址是相對於首選加載地址的,並且(隨機化)可能與實際加載地址不同。

在這里可以找到有關檢索正確地址的一些舊提示: http : //home.hiwaay.net/~georgech/WhitePapers/MapFiles/MapFiles.htm

通常,在編譯程序時,變量名不會保留。 如果您控制編譯過程,通常可以配置鏈接器和編譯器以生成一個映射文件,其中列出了所有全局變量在內存中的位置。 但是,在這種情況下,通過不使用直接內存訪問,而是創建一個外部程序可以調用的適當命令協議,您可以更輕松地實現目標。

如果您無法控制另一個程序的編譯過程,則可能很不走運,除非該程序附帶了映射文件或調試符號,而這兩個符號均可用於從變量地址中獲取變量名稱。 。

請注意,對於堆棧變量,派生其名稱將需要完整的調試符號,並且這是一個非常簡單的過程。 堆變量沒有名稱,因此自然不會有運氣。 此外,如@jdehaan的答案所述,在最佳情況下使用地圖文件可能有些棘手。 總而言之,最好具有適當的控制協議,以便完全避免依賴其他程序存儲器的內容。

最后,如果您無法控制其他程序,則建議將變量位置放入單獨的數據文件中。 這樣,您將不再需要每次都重新編譯,甚至可以支持所戳程序的多個版本。 如果您願意,您還可以使用某種自動更新服務從您的服務器中獲取此數據文件的新版本。

除非您實際擁有該應用程序,否則沒有標准的方法可以執行此操作。 如果您確實擁有該應用程序,則可以關注@jdehaan答案。

無論如何,為什么不將內存地址硬編碼到您的應用程序中,為什么不將一個簡單的feed托管在某個位置,您可以隨時使用目標應用程序的每個版本需要更改的內存地址進行更新? 這樣,您不必每次都重新編譯應用程序,而僅在需要能夠操縱新版本時更新該提要。

您不能直接執行此操作; 變量名稱實際上在編譯的二進制文件中不存在。 也許可以做到這一點,如果編寫程序,在說,Java或C#,它做存儲有關在編譯的二進制變量的信息。

此外,這通常是不可能的,因為目標程序內部值的最新副本始終位於CPU寄存器內部而不是內存中始終是可能的。 如果有問題的程序在發布模式下編譯且啟用了優化,則可能性更大。

如果可以確保目標程序是在調試模式下編譯的,則應該能夠使用編譯器發出的調試符號(.pdb文件),以便將地址映射到變量,但是在這種情況下,您將需要啟動目標程序處理過程就像被調試一樣-普通的讀取過程內存和寫入過程內存方法將不起作用。

最后,您的問題忽略了一個非常重要的考慮因素-即使存儲了此類信息,也不必存在與特定地址對應的變量。

如果您有相關應用程序的源代碼,並且不關心最佳內存使用情況,則可以在易於調試的結構中聲明有趣的變量,類似於:

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

現在,您的應用程序應該可以在程序的內存空間中搜索並查找head和tail標簽。 一旦找到您的可調試變量之一,它就可以使用var_namevalue成員來標識和修改它。

但是,如果要達到這個長度,最好啟用調試符號並使用常規調試器進行構建。

比利·奧尼爾(Billy O'Neal)開始朝着正確的方向前進,但(IMO)並未真正達到目標。 假設您的目標是Windows, SymFromName簡單的方法是使用Windows Symbol處理程序函數,尤其是SymFromName ,它可以讓您提供符號的名稱,並(除其他外)返回該符號的地址。

當然,要執行上述任何操作,您必須在允許進行調試的帳戶下運行。 但是,至少對於全局變量,您不必一定要停止目標進程才能找到符號,地址等。實際上,如果進程選擇使用它們,則可以在其自身上使用它們,這很好用。我的早期實驗中很少有人了解這些功能。 這是我幾年前編寫的一些演示代碼,至少給出了一個總體思路(盡管它已經足夠老,可以使用SymGetSymbolFromName ,它比SymFromName落后了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;
}

WinDBG有一個特別有用的命令

ln

這里

給定一個存儲位置,它將給出該位置的符號名稱。 具有正確的調試信息,它是調試器的(我是說進行調試的人:))。

這是我的系統(XP SP3)上的示例輸出

0:000> ln 7c90e514(7c90e514)
ntdll!KiFastSystemCallRet | (7c90e520)ntdll!KiIntSystemCall完全匹配:ntdll!KiFastSystemCallRet()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM