posted on Friday, January 16, 2004 4:06 PM by taylorza

Hacking my way across the process boundary

About 2 years ago a C++/MFC question appeared on the news groups asking how to send windows messages to a ListView control in another process and receive the data returned in the LVITEM structure. At the time I came up with what I thought to be an imaginative solution and it worked for the op. Recently this question reappeared but this time for the .NET environment, a quick Google search and I pointed the OP to the original post, leaving the conversion to .NET as an exercise for the reader. Since the routine did the job again and I remember having some fun playing around with the routine so I thought I would revive it here. Now I just need to get around to converting it to .NET :)

 

In short you allocate a block of memory in the target process using VirtualAllocEx, this block must be big enough to hold both the LVITEM structure and any string data you expect back. You also declare an instance of the structure in your local process and setup all the necessary fields; the only field that is configured differently is the pszText member, which is defined as pointing to the block of memory in the target process offset by the sizeof the LVITEM structure. Then the local structure is moved to the target process using WriteProcessMemory. Once all the structures are in place and in the correct process all that remains is to send the message using SendMessage, and passing the address of the structure in the remote process as the lParam. On successful return of the SendMessage the remote process should contain the requested data and you can copy the data back to the local process using ReadProcessMemory. Once again the pszText pointer has to be fixed up in the local process to reference the local buffer offset again by the sizeof LVITEM.

 

The following C++ code provides an example of getting a item from a ListView hosted in another process.

 

  const DWORD dwBufSize = 1024;

 

  DWORD dwProcessID;

  DWORD dwResult;

  HANDLE hProcess;

 

  BYTE *lpRemoteBuffer;

 

  LVITEM lvItem = {0};

 

  BYTE lpLocalBuffer[dwBufSize] = {0};

 

  // Get the process id owning the window

  ::GetWindowThreadProcessId( hwndListView, &dwProcessID );

 

  // Open the process wih all access (You may not have the rights to do this)

  hProcess = ::OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcessID );

 

  // Allocate a buffer in the remote process

  lpRemoteBuffer = (BYTE*)::VirtualAllocEx( hProcess, NULL, dwBufSize,

  MEM_COMMIT, PAGE_READWRITE );

 

  // Fill in the LVITEM struct, this is in your own process

  // Set the pszText member to somewhere in the remote buffer,

  // For the example I used the address imediately following the LVITEM stuct

  lvItem.mask = LVIF_TEXT;

  lvItem.iItem = 0;

  lvItem.cchTextMax = 50;

  // Point to after LVITEM in the remote buffer

  lvItem.pszText = (LPTSTR)(lpRemoteBuffer + sizeof( LVITEM ));

 

  // Copy the local LVITEM to the remote buffer

  ::WriteProcessMemory( hProcess, (LPVOID)lpRemoteBuffer,

    &lvItem, sizeof(LVITEM), NULL );

 

  // Send the message

  ::SendMessage( hwndListView, LVM_GETITEM, 0, (LPARAM)lpRemoteBuffer);

 

  // Read the struct back from the remote process into local buffer

  ::ReadProcessMemory( hProcess, (LPVOID)lpRemoteBuffer, lpLocalBuffer,

    dwBufSize, NULL );

 

  //Fix pszText to point to same offset in local buffer

  lvItem.pszText = (LPTSTR)(lpLocalBuffer + sizeof( LVITEM ));

 

  // Clean-up

  ::VirtualFreeEx( hProcess, (LPVOID)lpRemoteBuffer, 0, MEM_RELEASE );

  ::CloseHandle( hProcess );

Comments