How can I get the values of the local variables via DbgHelp

559 views Asked by At

How can I get the values of the local variables via DbgHelp? I've tried to use the following code

#include <boost/scope_exit.hpp>

#include <Windows.h>
#include <Dbghelp.h>

#include <iostream>
#include <map>
#include <string>

#pragma comment(lib, "Dbghelp.lib")

BOOL CALLBACK enum_symbols_callback(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext)
{
  if (SymbolSize == 0)
  {
    return TRUE;
  }

  auto* local_variables_info = reinterpret_cast<std::map<std::string, std::string>*>(UserContext);

  std::unique_ptr<unsigned char[]> bytes_read(new unsigned char[SymbolSize]);
  HANDLE cur_process_handle = GetCurrentProcess();
  SIZE_T number_of_bytes_actually_read;
  if (ReadProcessMemory(
    cur_process_handle
    , reinterpret_cast<void*>(pSymInfo->Address)
    , bytes_read.get()
    , SymbolSize
    , &number_of_bytes_actually_read) != 0)
  {
    unsigned char* bytes = bytes_read.get();
    local_variables_info->operator[](pSymInfo->Name) = std::to_string(pSymInfo->Value);
  }

  return TRUE; // Continue enumeration
}

std::map<std::string, std::string> get_local_variables_info()
{
  std::map<std::string, std::string> local_variables_info;

  HANDLE cur_process_handle = GetCurrentProcess();
  if (SymInitialize(cur_process_handle, NULL, TRUE) == FALSE)
  {
    return local_variables_info;
  }
  BOOST_SCOPE_EXIT_ALL(cur_process_handle)
  {
    SymCleanup(cur_process_handle);
  };

  const ULONG frames_to_skip = 0;
  const ULONG frames_to_capture = 1;
  void* stack[frames_to_capture];

  const USHORT frames = CaptureStackBackTrace(
    frames_to_skip
    , frames_to_capture
    , stack
    , NULL
  );
  if (frames != 1)
  {
    return local_variables_info;
  }

  IMAGEHLP_STACK_FRAME sf;
  sf.InstructionOffset = reinterpret_cast<DWORD_PTR>(stack[0]);
  if (SymSetContext(
    cur_process_handle,
    &sf,                  // The context 
    0                     // Not used 
  ) == FALSE)
  {
    return local_variables_info;
  }

  if (SymEnumSymbols(
    cur_process_handle,
    0,                      // 0 -> SymEnumSymbols will use the context set with SymSetContext
    0,                      // Mask must also be 0 to use the context
    enum_symbols_callback,
    &local_variables_info   // User-defined context
  ) == FALSE)
  {
    return local_variables_info;
  }

  return local_variables_info;
}

int main()
{
  int foo = 0;
  const auto& local_variables_info = get_local_variables_info();
  for (const std::pair<std::string, std::string> e : local_variables_info)
  {
    std::cout << e.first << ' ' << e.second << '\n';
  }
}

but reinterpret_cast<void*>(pSymInfo->Address) always returns address like FFFFFFD4.

Why? What am I doing wrong? How can I fix it?

Thanks in advance.

1

There are 1 answers

1
Grak On

The address of the variable's value you are looking for is at the address of the start of the function (accessible from EBP register of the execution context), applied from the offset of this variable location from the function in the stack. In your case (variable local to a function), pSymInfo->Address member stores the offset of the variable relative to the stack frame.

(An explanation here : http://eli.thegreenplace.net/2011/02/04/where-the-top-of-the-stack-is-on-x86/)

So the address you are looking for is at (EBP + pSymInfo->Address)

To get the start of the function, you can use :

  1. CONTEXT -> EBP register
  2. StackWalk64() -> enables to walk callstack
  3. GetThreadContext() -> to get the context