1

第一次在这里发布这么多我的谷歌结果来自这个精彩的网站。

基本上,我想找到存储在特定内存地址的变量的名称。我有一个我写的内存编辑应用程序,它编辑一个值,问题是每次保存这个值的应用程序被修补时,我必须将新的内存地址硬编码到我的应用程序中,然后重新编译,这需要很长时间维护它几乎不值得做。

我想做的是获取存储在某个内存地址的变量的名称,这样我就可以在运行时找到它的地址并将其用作要编辑的内存地址。

这一切都是用 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 变量有关。如果扩展我的问题的使用违反了规则,我深表歉意,如果是,我将删除我的编辑。

4

7 回答 7

7

编译并生成一个所谓的map文件。这可以使用 Visual-C++(/MAP链接器选项)轻松完成。在那里您会看到符号(函数,...)及其起始地址。使用此映射文件(注意:每次重新编译时都必须更新),您可以将地址与名称匹配。

这实际上并不容易,因为地址是相对于首选加载地址的,并且可能(随机化)与实际加载地址不同。

可以在此处找到有关检索正确地址的一些旧提示:http: //home.hiwaay.net/~georgech/WhitePapers/MapFiles/MapFiles.htm

于 2010-08-23T21:48:13.593 回答
3

通常,编译程序时不会保留变量的名称。如果您可以控制编译过程,通常可以配置链接器和编译器以生成一个映射文件,列出所有全局变量在内存中的位置。但是,如果是这种情况,您可能可以通过不使用直接内存访问更轻松地实现您的目标,而是创建一个您的外部程序可以调用的适当命令协议。

如果您无法控制其他程序的编译过程,那么您可能不走运,除非该程序附带映射文件或调试符号,其中任何一个都可用于从变量的地址派生变量的名称.

请注意,对于堆栈变量,派生它们的名称将需要完整的调试符号,并且是一个非常重要的过程。堆变量没有名字,所以你自然不会走运。此外,正如@jdehaan 的回答中提到的,在最好的情况下使用地图文件可能有点棘手。总而言之,最好有一个适当的控制协议,您可以使用它来完全避免对其他程序内存内容的任何依赖。

最后,如果您无法控制其他程序,那么我建议将变量位置放入单独的数据文件中。这样你就不再需要每次都重新编译,甚至可以支持被戳的程序的多个版本。如果您愿意,您还可以使用某种自动更新服务从您的服务器中提取此数据文件的新版本。

于 2010-08-23T21:53:34.893 回答
1

除非您实际上拥有相关应用程序,否则没有标准的方法可以做到这一点。如果您确实拥有该应用程序,则可以关注@jdehaan 的回答。

在任何情况下,与其将内存地址硬编码到您的应用程序中,为什么不在某个地方托管一个简单的提要,您可以随时使用您需要为每个目标应用程序版本更改的内存地址进行更新?这样,您无需每次都重新编译您的应用程序,而是在需要能够操作新版本时更新该提要。

于 2010-08-23T21:53:28.447 回答
1

你不能直接这样做;变量名实际上并不存在于编译的二进制文件中。如果程序是用 Java 或 C# 编写的,那么您也许可以做到这一点,它们确实将有关变量的信息存储在已编译的二进制文件中。

此外,这通常是不可能的,因为目标程序中值的最新副本总是可能位于 CPU 寄存器内部而不是内存中。如果有问题的程序是在发布模式下编译的,并且启用了优化,则这种情况更有可能发生。

如果您可以确保目标程序在调试模式下编译,您应该能够使用编译器发出的调试符号(.pdb 文件)将地址映射到变量,但在这种情况下,您需要启动目标进程就像正在调试一样——普通的读取进程内存和写入进程内存方法不起作用。

最后,您的问题忽略了一个非常重要的考虑因素——即使存储了此类信息,也不需要与特定地址对应的变量。

于 2010-08-23T21:54:08.860 回答
0

如果您有相关应用程序的源代码并且不需要考虑最佳内存使用情况,那么您可以在调试友好的结构中声明有趣的变量,类似于:

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_namevalue成员来识别和修改它。

但是,如果您要达到这个长度,您可能最好在启用调试符号并使用常规调试器的情况下进行构建。

于 2010-08-23T22:05:02.590 回答
0

比利奥尼尔开始朝着正确的方向前进,但(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;
}
于 2010-08-23T23:17:24.093 回答
0

WinDBG 有一个特别有用的命令

ln

这里

给定一个内存位置,它将给出该位置的符号名称。有了正确的调试信息,它就是一个调试器(我的意思是做调试的人:))恩惠!。

这是我系统上的示例输出(XP SP3)

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

于 2010-08-24T07:05:16.603 回答