Console Application to discover Effective Named Pipe Path of a WCF net.pipe Endpoint (Update for .NET 4.0+)


 

Named pipes created by WCF does not look like the endpoint at all. I discuss this at length here:

Named Pipes in WCF are named but not by you (and how to find the actual windows object name)

I wrote an console application to test it and I detailed this here:

Console Application to discover Effective Named Pipe Path of a WCF net.pipe Endpoint

However, starting with .NET 4.0, the shared memory object changed from Local to the session to Global. Session in this context is the application session. Starting with Vista, services stopped running in the same session as the desktop session. So, Services interacting with the desktop are things of the past. Services now run on session 0 and desktop applications which enable user interaction normally run on session 2. Because of this, the shared memory object moved to local (session specific) to global. This requires that applications creating a named pipe endpoint run in a security context that includes the permission seCreateGlobalObjects. This also is valid for the context of the client application trying to read the shared memory object. I updated the application to also read the global object, The files include source and executable can be downloaded here:

WCF Named Pipes Identification

The source code is below:

   1: // ReadMemory.cpp : Defines the entry point for the console application.
   2: //
   3:  
   4: #include "stdafx.h"
   5:  
   6:  
   7: void PrintError(LPTSTR lpszFunction) 
   8: { 
   9:     // Retrieve the system error message for the last-error code
  10:  
  11:     LPVOID lpMsgBuf;
  12:     LPVOID lpDisplayBuf;
  13:     DWORD dw = GetLastError(); 
  14:     if(dw == 0)
  15:         return;
  16:     FormatMessage(
  17:         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
  18:         FORMAT_MESSAGE_FROM_SYSTEM |
  19:         FORMAT_MESSAGE_IGNORE_INSERTS,
  20:         NULL,
  21:         dw,
  22:         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  23:         (LPTSTR) &lpMsgBuf,
  24:         0, NULL );
  25:  
  26:  
  27:  
  28:     lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
  29:         (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
  30:     StringCchPrintf((LPTSTR)lpDisplayBuf, 
  31:         LocalSize(lpDisplayBuf) / sizeof(TCHAR),
  32:         TEXT("%s failed with error %d: %s"), 
  33:         lpszFunction, dw, lpMsgBuf); 
  34:     std::wprintf(L"\n***NON-FATAL ERROR: %s\n", (LPCTSTR)lpDisplayBuf); 
  35:  
  36:     LocalFree(lpMsgBuf);
  37:     LocalFree(lpDisplayBuf);
  38: }
  39:  
  40:  
  41: void NormalizeEndPoint(const std::wstring &source, std::wstring& normal)
  42: {
  43:     normal.assign(source);
  44:  
  45:     if(!normal.compare(0, 10, L"net.pipe://"))
  46:     {
  47:         normal.clear();
  48:         return;
  49:     }
  50:  
  51:     int hoststart = normal.find_first_of(L"//");
  52:     int hostend = normal.find(L"/", hoststart+2);
  53:  
  54:     std::wstring ending(normal.substr(hostend));
  55:     std::wstring starting(normal.substr(0, hoststart).append(L"//+"));
  56:     std::transform(ending.begin(), ending.end(), ending.begin(), (int(*)(int))std::toupper);
  57:     std::wstring normalized(starting.append(ending));
  58:     if(normalized.substr(normalized.length()-1).compare(L"/"))
  59:     {
  60:         normalized.append(L"/");
  61:     }
  62:     
  63:     normal.assign(normalized);
  64:     return;
  65:  
  66: }
  67:  
  68: void ShowHeader()
  69: {
  70:     std::wprintf(L"ReadMemory version 2.0\n");
  71:     std::wprintf(L"Written by Rodney Viana - http://blogs.msdn.com/b/rodneyviana\n");
  72: }
  73:  
  74: void ShowSyntax(bool SyntaxError)
  75: {
  76:     std::wprintf(L"\n");
  77:     if(SyntaxError)
  78:         std::wprintf(L"Syntax Error\n\n");
  79:  
  80:     std::wprintf(L"Syntax:\n");
  81:     std::wprintf(L"ReadMemory <PipeNameEndPoint> | -file <MappedMemoryFile>\n");
  82:     std::wprintf(L"Where:\t<PipeNameEndPoint> is a endpoint for a net.pipe in WCF in\n\t the format net.pipe://host/path\n");
  83:     std::wprintf(L"\t<MappedMemoryFile> is a memory mapped file\n");
  84:     std::wprintf(L"\n");
  85:     std::wprintf(L"Examples\n");
  86:     std::wprintf(L"\tReadMemory net.pipe://localhost/Service/Service1\n");
  87:     std::wprintf(L"\tReadMemory -file \"net.pipe:EbmV0LnBpcGU6Ly8rLzhFNjFFRUM5LUYxOUEtNEIxNy04REE4LTM5NTc1QzhGMTU4QS8=\"\n");
  88:  
  89: }
  90:  
  91: int _tmain(int argc, _TCHAR* argv[])
  92: {
  93:     ShowHeader();
  94:     if(argc < 2)
  95:     {
  96:         ShowSyntax(false);
  97:         return 0;
  98:     }
  99:  
 100:     if(argc > 3)
 101:     {
 102:         ShowSyntax(true);
 103:         return 1;
 104:  
 105:     }
 106:  
 107:     std::wstring original;
 108:     std::wstring normalized;
 109:     std::wstring mapFile;
 110:  
 111:  
 112:     if(argc == 2)
 113:     {
 114:         original.append(argv[1]);
 115:         NormalizeEndPoint(original, normalized);
 116:         std::wprintf(L"\nOriginal Endpoint: %s", original.c_str());
 117:  
 118:         std::wprintf(L"\nNominal Endpoint: %s", normalized.c_str());
 119:  
 120:         char base64A[1000];
 121:  
 122:         CW2A ansiNormal(normalized.c_str());
 123:         int size = 1000;
 124:     
 125:         Base64Encode((BYTE*)ansiNormal.m_psz, normalized.length(), base64A, &size, ATL_BASE64_FLAG_NOCRLF | ATL_BASE64_FLAG_NOPAD );
 126:  
 127:         base64A[size]='=';
 128:         base64A[size+1]='';
 129:  
 130:         CA2W base64W(base64A);
 131:  
 132:         mapFile.append(L"net.pipe:E");
 133:         mapFile.append(base64W.m_psz);
 134:  
 135:     }
 136:  
 137:     if(argc == 3)
 138:     {
 139:         std::wstring force(argv[1]);
 140:         std::transform(force.begin(), force.end(), force.begin(), (int(*)(int))std::toupper);
 141:         if(!force.compare(0, 4, L"-FILE"))
 142:         {
 143:             ShowSyntax(true);
 144:             return 2;
 145:         }
 146:  
 147:         mapFile.append(argv[2]);
 148:  
 149:     }
 150:  
 151:     //original.append(L"net.pipe://localhost/TradeService/Service1");
 152:  
 153:  
 154:     std::wprintf(L"\nMapped Memory Object Name (Local): %s", mapFile.c_str());
 155:  
 156:     HANDLE map = OpenFileMapping(FILE_MAP_READ, FALSE, mapFile.c_str());
 157:     if(!map)
 158:     {
 159:         PrintError(L"Opening Local Mapped file");
 160:         std::wprintf(L"WARNING: Local mapped file object was not found. We will now try to find the global file\n");
 161:         std::wstring global = L"Global\\";
 162:         global.append(mapFile);
 163:         mapFile = global;
 164:  
 165:         std::wprintf(L"\nMapped Memory Object Name (Global): %s", mapFile.c_str());
 166:  
 167:         map = OpenFileMapping(FILE_MAP_READ, FALSE, mapFile.c_str());
 168:  
 169:  
 170:     }
 171:     MEMORY_BASIC_INFORMATION mi;
 172:     if(map)
 173:     {
 174:         std::wprintf(L"\nGood News: Mapped memory Object was found. It shows that Host is enabled.");
 175:  
 176:         PVOID contents = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
 177:         SIZE_T s = VirtualQuery(contents, &mi, sizeof(mi));
 178:  
 179:         if(s<sizeof(GUID)+4)
 180:         {
 181:             std::wprintf(L"\nMapped memory Object seems to be corrupted. Restart your WCF Host.\n");
 182:         }
 183:         
 184:         PBYTE bytes = ((PBYTE)contents)+4;
 185:  
 186:  
 187:         std::wprintf(L"\nRaw Bytes:\n");
 188:  
 189:  
 190:         if(contents)
 191:         {
 192:             for(SIZE_T i=0;i<sizeof(GUID);i++)
 193:             {
 194:                 printf("%02x ", *(bytes+i));
 195:             }
 196:  
 197:             std::wprintf(L"\n");
 198:             
 199:             for(SIZE_T i=sizeof(GUID);i<s-4;i++)
 200:             {
 201:                 printf("%02x ", *(bytes+i));
 202:             }
 203:         
 204:             GUID *guid = (GUID*)bytes;
 205:             RPC_WSTR guidStr;
 206:  
 207:  
 208:             UuidToString(guid, &guidStr);
 209:             std::wprintf(L"\nActual Named Pipe Name: %s", guidStr);
 210:             std::wprintf(L"\nFull Named Pipe Name: \\Device\\NamedPipe\\%s", guidStr);
 211:             
 212:             std::wstring localPipe(_T("\\\\.\\pipe\\"));
 213:             localPipe.append((LPWSTR)guidStr);
 214:  
 215:  
 216:  
 217:             std::wprintf(L"\nAttempting to connect to Named Pipe for 20 seconds ...\n");
 218:  
 219:             if(!WaitNamedPipe(localPipe.c_str(), 20000))
 220:             {
 221:                 std::wprintf(L"\nBad News: Unable to connect to local pipe: %s.\nReason: Time out.", localPipe.c_str());
 222:  
 223:             } else
 224:             {
 225:                 std::wprintf(L"\nGood News: Local pipe \"%s\" is alive.", localPipe.c_str());
 226:             
 227:                 HANDLE pipe;
 228:  
 229:                 std::wprintf(L"\nAttempting to open Named Pipe...\n");
 230:  
 231:                 pipe = CreateFile(localPipe.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);
 232:  
 233:             
 234:                 if(pipe == INVALID_HANDLE_VALUE)
 235:                 {
 236:                     std::wprintf(L"\nBad News: Unable to open local pipe: %s.\n", localPipe.c_str());
 237:                     PrintError(L"Pipe Error");
 238:                 } else
 239:                 {
 240:                     std::wprintf(L"\nGood News: Named Pipe opened successfully.\n");
 241:  
 242:                     TCHAR* send = L"<bad><\\bad>\n";
 243:                     DWORD bytesUsed;
 244:  
 245:                     if(!WriteFile(pipe, (void*)send, wcslen(send)*sizeof(TCHAR), &bytesUsed, NULL))
 246:                     {
 247:                         std::wprintf(L"\nBad News: Unable to send bytes.\n");
 248:  
 249:                     } else
 250:                     {
 251:                         std::wprintf(L"\nGood News: Pipe accepted bytes\n");
 252:                         std::wprintf(L"\nTest completed successfully!\n");
 253:  
 254:                     }
 255:                     CloseHandle(pipe);
 256:                 }
 257:  
 258:             }
 259:  
 260:         
 261:         } else
 262:         {
 263:             std::wprintf(L"\nBad News: Host is not informing pipe id.");
 264:             
 265:         }
 266:         UnmapViewOfFile(map);
 267:     } else
 268:     {
 269:         PrintError(L"Opening Global Mapped File");
 270:         std::wprintf(L"\nBad News: Mapped File %s was not found.\n", mapFile.c_str());
 271:  
 272:  
 273:     }
 274:     
 275:     CloseHandle(map);
 276:     std::wprintf(L"\n");
 277:     return 0;
 278: }
 279:  

Comments (3)

  1. Motaz says:

    Hi Rodney,

    Thanks for sharing this information.

    Is it possible to connect to a local named pipe created in session 2 (Not using seCreateGlobalObjects) from a process running in session 0 using SYSTEM account context?

    Best,

    Moataz

  2. Motaz,

    Quick answer: Maybe Not. I wrote a KB for CRM plugin in Outlook that hangs. This KB gives you some troubleshooting techniques (using procexp from SysInternals). After this analysis we can be more precise. Also I will give you instructions on how to capture ETL traces and see what is happening.

Skip to main content