Try to access the environment variables of a current running proces? The following is close but no cigar. Any help to go further. I know it has to do with the PROCES_PARAMETERS contained within the Process Environment Block PEB. But I am completely lost at this point. the following returns the command line of the running process, but I really need to capture the env variables.

BTW, credit goes to other developers for the following.

Thanks in advance.
Regards.

typedef long NTSTATUS;

#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

typedef enum _PROCESSINFOCLASS { ProcessBasicInformation } PROCESSINFOCLASS;

typedef struct _INFOBLOCK
{
    unsigned long dwFiller[16];
    unsigned short wLength;
    unsigned short wMaxLength;
    const unsigned short *dwCmdLineAddress;
    const unsigned short *env;
} INFOBLOCK, *PINFOBLOCK;

typedef struct _PEB
{
    unsigned long dwFiller[4];
    PINFOBLOCK dwInfoBlockAddress;
} PEB, *PPEB;

typedef struct _PROCESS_BASIC_INFORMATION
{
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    unsigned long AffinityMask;
    long BasePriority;
    unsigned long UniqueProcessId;
    unsigned long InheritedFromUniqueProcessId;
} PBI;

typedef NTSTATUS (NTAPI *ZWQueryInformationProcessW)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
void Get(HANDLE hProcess)
{
    ZWQueryInformationProcessW ZwQueryInformationProcessA;

    HMODULE hModule = GetModuleHandle(_T("ntdll"));

    ZwQueryInformationProcessA = (ZWQueryInformationProcessW)GetProcAddress(hModule, "ZwQueryInformationProcess");

    if (ZwQueryInformationProcessA == NULL) exit(1);

    PBI ProcInfo;
    PEB ProcPEB;
    INFOBLOCK ProcBlock;
    unsigned long ReturnLength;
    //HANDLE hProcess;
    unsigned short *pszCmdLine = NULL;
    int bSuccess;

    //hProcess = GetCurrentProcess();
    //hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, 2780);

    if (! NT_SUCCESS(ZwQueryInformationProcessA(hProcess, ProcessBasicInformation, &ProcInfo, sizeof(ProcInfo), &ReturnLength))) exit(1);

    bSuccess = ReadProcessMemory(hProcess, (const void *)ProcInfo.PebBaseAddress, &ProcPEB, sizeof(ProcPEB), &ReturnLength);

    if (bSuccess != false)
    {
        bSuccess = ReadProcessMemory(hProcess, (const void *)ProcPEB.dwInfoBlockAddress, &ProcBlock, sizeof(ProcBlock), &ReturnLength);

        pszCmdLine = (unsigned short *) new BYTE[ProcBlock.wMaxLength];
    }

    if (bSuccess != false)
    {
        bSuccess = ReadProcessMemory(hProcess, ProcBlock.dwCmdLineAddress, pszCmdLine, ProcBlock.wMaxLength, &ReturnLength);
    }

    _tprintf(TEXT("%S\n"),pszCmdLine); 

    if (NULL != pszCmdLine) delete [] pszCmdLine;

    // CloseHandle(hProcess);

    return;
}
int main()
{
    // Get the list of process identifiers.
    unsigned long processID[1024];
    unsigned long size;
    unsigned long n_processID;
    char szProcessName[MAX_PATH] = TEXT("<unknown>");
    HANDLE hProcess;
    HMODULE hModule;
    std::vector<std::pair<unsigned long, std::string> > processes;

    if (! EnumProcesses(processID, sizeof(processID), &size)) return -1;

    // Calculate how many process identifiers were returned.
    n_processID = size / sizeof(unsigned long);

    // Print the name and process identifier for each process.
    for (unsigned ii = 0; ii < n_processID; ++ii)
    {
        if (processID[ii] == 0) continue;
        
        // Get a handle to the process.
        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processID[ii]);

        // Get the process name.
        if (hProcess == NULL) continue;

        if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &size))
        {
            GetModuleBaseName(hProcess, hModule, szProcessName, sizeof(szProcessName)/sizeof(char));

            processes.push_back(std::make_pair(processID[ii], szProcessName));
        }

        if (! stricmp(szProcessName, "cmd.exe"))
        {
            Get(hProcess);

            std::cout << "Found PID: " << processID[ii] << std::endl;
        }

        //_tprintf(TEXT("%s  (PID: %u)\n"), szProcessName, processID[ii]);

        CloseHandle(hProcess);
    }

    std::sort(processes.begin(), processes.end());

    return 0;
}

Recommended Answers

All 10 Replies

this is what Pietrek suggests:

#define _WIN32_WINNT  0x0501
#define NO_STRICT
#include <windows.h>
#include <winternl.h>
#include <iostream>
#include <cassert>

wchar_t*  GetEnvironmentStringsW( HANDLE  hproc )
{
  void* p_ntdll = GetModuleHandle( L"ntdll.dll" ) ;
  typedef NTSTATUS (__stdcall* tfn_qip ) ( HANDLE,
                    PROCESSINFOCLASS, void*, ULONG, PULONG ) ;
  tfn_qip pfn_qip = tfn_qip( GetProcAddress( p_ntdll, 
                     "NtQueryInformationProcess" ) ) ;
  assert( pfn_qip ) ;
  PROCESS_BASIC_INFORMATION pbi ;
  ULONG res_len = 0 ;
  NTSTATUS status = pfn_qip( hproc,  ProcessBasicInformation,
                     &pbi, sizeof(pbi), &res_len ) ;
  assert( res_len == sizeof(pbi) ) ;
  size_t ppeb = size_t( pbi.PebBaseAddress ) ;

  char peb[ sizeof(PEB) ] ;
  DWORD read ;
  ReadProcessMemory( hproc, pbi.PebBaseAddress, 
                           peb, sizeof(peb), &read ) ; 
  assert( read == sizeof(peb) ) ;

  enum { OFFSET_PEB = 0x10, OFFSET_X = 0x48 };
  
  void* ptr = (void*) *(INT_PTR*)(peb + OFFSET_PEB ) ;
  char buffer[ OFFSET_X + sizeof(void*) ] ;
  ReadProcessMemory( hproc, ptr, buffer, sizeof(buffer), &read ) ; 
  assert( read == sizeof(buffer) ) ;
  
  void* penv = (void*) *(INT_PTR*)( buffer + OFFSET_X ) ;
  enum { MAX_ENV_SIZE = 4096 }; 
  wchar_t* env = new wchar_t[ MAX_ENV_SIZE ] ;
  ReadProcessMemory( hproc, penv, env, MAX_ENV_SIZE, &read ) ; 
  assert( read > 0 ) ;
  
  return env ;
}

// small test driver
int main()
{
  HWND hwnd = FindWindow( 0, L"Solitaire" ) ;
  assert( hwnd ) ;
  DWORD procid = 0 ;
  GetWindowThreadProcessId( hwnd, &procid ) ;
  assert( procid ) ;
  DWORD amask = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ ;
  HANDLE hproc = OpenProcess( amask, 0, procid ) ;
  assert( hproc ) ;
  
  wchar_t* env = GetEnvironmentStringsW( hproc ) ;

  wchar_t* pwstr = env  ;
  do
  {
    std::wcout << pwstr << '\n' ;
    pwstr += wcslen(pwstr) + 1 ;
  }while( *pwstr ) ;

  delete[] env  ;
}

vijayan121, thank you very much for the rapid reply.

And the sample code.
I learn best from sample code and I progress from there. Its kind of a kick in the butt in the right direction.

This appears to be exactly what I was trying to accomplish, its different then I expected but it works perfectly. I will now study it to understand exactly what it is doing. I needed the help but I never use code in production applications until I fully grasp what it is doing. I have to continue to maintain it for the 20 years and this is for General Motors, so it critical I have a full understanding since it will be global in the next few weeks.

Thank you again. I am very grateful! :)

Regards.

BTW, do you know of any good books or sites that extensively cover WIN32 API odd ball type code, also I am in need of an advance C++ code reference? Advance being the key word.

Any recommendations?

Thanks again.

Regards.

> do you know of any good books or sites that extensively cover WIN32 API odd ball type code
i don't program very much in the win32 environment. the only one i can think of on the spur of the moment is sysinternals
http://forum.sysinternals.com/forum_topics.asp?FID=9
there would be many others which i am not aware of.

> I am in need of an advance C++ code reference? Advance being the key word.
do you mean as a language/library reference? if it is *one* book you are looking for, i don't think that Stroustrup's The C++ Programming Language (Special Edition) can be bettered. a c++ programmer can use this as a reference. It is also a pretty good tutorial (if you are not a beginner).
if you were looking for more than one book, these are some of the the books (which come to mind right now) that i've liked.
1. Ruminations on C++: A Decade of Programming Insight and Experience by Andrew Koenig, Barbara E. Moo
2. The Design and Evolution of C++ by Bjarne Stroustrup
3. Advanced C++ Programming Styles and Idioms by James O. Coplien
4. Inside the C++ Object Model by Stanley B. Lippman
5. C++ Templates: The Complete Guide by David Vandevoorde, Nicolai M. Josuttis
6. Modern C++ Design: Generic Programming and Design Patterns Applied by Andrei Alexandrescu
7. C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond by David Abrahams, Aleksey Gurtovoy
8. C++ Coding Standards: 101 Rules, Guidelines, and Best Practices by Herb Sutter, Andrei Alexandrescu
9. Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference by Angelika Langer, Klaus Kreft
10. The C++ Standard Library: A Tutorial and Reference by Nicolai M. Josuttis
11. Large-Scale C++ Software Design by John Lakos
12. Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
13. Design Patterns Explained: A New Perspective on Object-Oriented Design (2nd Edition) by Alan Shalloway, James Trott
14. Refactoring: Improving the Design of Existing Code by Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts
15. Refactoring to Patterns by Joshua Kerievsky

these c++ newsgroups may interest you:
comp.lang.c++.moderated http://groups.google.co.in/group/comp.lang.c++.moderated/topics
comp.std.c++ http://groups.google.com/group/comp.std.c++/topics

for non-beginners, most forums are of little use. this one could be the exception.
velociy reviews http://www.velocityreviews.com/forums/f39-c.html

Help again.
I'm using this code to capture the environment variables from the PEB, all seems to work except one issue. Due to the number of environment variables the process I query has, I had to enlarge this value, MAX_ENV_SIZE. If I set it too high, I get none, set it too low, I don't get them all. By trail and error, I find an exact size that gets me all the variables. But these may be added to or removed in the future. I really don't want to constantly redeploy the application just because support changed the environment variables by adding or removing any of them. Is there another method to set the whchar_t *env size? Would HeapAlloc or VirtualAlloc be an option here, I have never used either of these? Any ideals?

void* penv = (void*) *(INT_PTR*)( buffer + OFFSET_X ) ;
[B]enum { MAX_ENV_SIZE = 4096 };[/B]
wchar_t* env = new wchar_t[ MAX_ENV_SIZE ] ;
ReadProcessMemory( hproc, penv, env, MAX_ENV_SIZE, &read ) ;
assert( read > 0 ) ;

this is what Pietrek suggests:
.....
lots of C++ code
.....

That is very helpful and seems to work well on XP32, but it fails on XP64

ReadProcessMemory( hproc, pbi.PebBaseAddress, peb, sizeof(peb), &read ) ;
assert( read == sizeof(peb) ) ;
this assert fails


Any help?

TIA,
wezt

Write a trivial program that calls GetEnvironmentStringsW, and step into it in the debugger’s assembly window mode. You’ll see code that looks like this:

mov  eax,fs:[0x18]              
  mov  eax,dword ptr [eax+0x30]   
  mov  eax,dword ptr [eax+0x10]   
  mov  eax,dword ptr [eax+0x48]   
  ret

It’s relatively well known (including info from the Platform SDK’s WINTERNL.H file) that FS:[0x18] points to a thread’s Thread Environment Block (or TEB). Offset 0x30 within the TEB points to the Process Environment Block (or PEB.).

Knowing that the first two instructions of GetEnvironmentStringW put the address of the PEB into EAX, what can we infer? The third instruction grabs a DWORD value from offset 0x10 in the PEB. It must be a pointer to a structure of some kind because the fourth instruction dereferences that value (+ 0x48). ...

from Matt Pietrek's blog entry on which the (32 bit) code is `based

obviously, this is 32-bit code (and the PEB referred to is the PEB for 32-bit) and will not work on a 64-bit implementation. but you could use a similar technique to figure out what the offsets are for 64-bit XP.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.